line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Chart::Clicker; |
2
|
|
|
|
|
|
|
$Chart::Clicker::VERSION = '2.90'; |
3
|
7
|
|
|
7
|
|
156078
|
use Moose; |
|
7
|
|
|
|
|
891518
|
|
|
7
|
|
|
|
|
50
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
extends 'Chart::Clicker::Container'; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# ABSTRACT: Powerful, extensible charting. |
8
|
|
|
|
|
|
|
|
9
|
7
|
|
|
7
|
|
34056
|
use Layout::Manager::Compass; |
|
7
|
|
|
|
|
158357
|
|
|
7
|
|
|
|
|
292
|
|
10
|
|
|
|
|
|
|
|
11
|
7
|
|
|
7
|
|
3287
|
use Graphics::Color::RGB; |
|
7
|
|
|
|
|
3016198
|
|
|
7
|
|
|
|
|
436
|
|
12
|
|
|
|
|
|
|
|
13
|
7
|
|
|
7
|
|
1660
|
use Graphics::Primitive::Insets; |
|
7
|
|
|
|
|
293127
|
|
|
7
|
|
|
|
|
286
|
|
14
|
7
|
|
|
7
|
|
1815
|
use Graphics::Primitive::Border; |
|
7
|
|
|
|
|
297757
|
|
|
7
|
|
|
|
|
217
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#use Graphics::Primitive::Driver::Cairo; |
17
|
|
|
|
|
|
|
|
18
|
7
|
|
|
7
|
|
4259
|
use Chart::Clicker::Context; |
|
7
|
|
|
|
|
31
|
|
|
7
|
|
|
|
|
412
|
|
19
|
|
|
|
|
|
|
|
20
|
7
|
|
|
7
|
|
1960
|
use Chart::Clicker::Data::DataSet; |
|
7
|
|
|
|
|
18
|
|
|
7
|
|
|
|
|
265
|
|
21
|
7
|
|
|
7
|
|
1966
|
use Chart::Clicker::Data::Series; |
|
7
|
|
|
|
|
133
|
|
|
7
|
|
|
|
|
246
|
|
22
|
7
|
|
|
7
|
|
5005
|
use Chart::Clicker::Decoration::Legend; |
|
7
|
|
|
|
|
28
|
|
|
7
|
|
|
|
|
395
|
|
23
|
7
|
|
|
7
|
|
5163
|
use Chart::Clicker::Decoration::MarkerOverlay; |
|
7
|
|
|
|
|
29
|
|
|
7
|
|
|
|
|
426
|
|
24
|
7
|
|
|
7
|
|
5259
|
use Chart::Clicker::Decoration::Plot; |
|
7
|
|
|
|
|
29
|
|
|
7
|
|
|
|
|
402
|
|
25
|
7
|
|
|
7
|
|
5105
|
use Chart::Clicker::Drawing::ColorAllocator; |
|
7
|
|
|
|
|
25
|
|
|
7
|
|
|
|
|
367
|
|
26
|
|
|
|
|
|
|
|
27
|
7
|
|
|
7
|
|
62
|
use Carp qw(croak); |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
539
|
|
28
|
7
|
|
|
7
|
|
40
|
use Scalar::Util qw(refaddr); |
|
7
|
|
|
|
|
27
|
|
|
7
|
|
|
|
|
16883
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
has '+background_color' => ( |
32
|
|
|
|
|
|
|
default => sub { |
33
|
|
|
|
|
|
|
Graphics::Color::RGB->new({ red => 1, green => 1, blue => 1, alpha => 1 }) |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
has '+border' => ( |
39
|
|
|
|
|
|
|
default => sub { |
40
|
|
|
|
|
|
|
my $b = Graphics::Primitive::Border->new; |
41
|
|
|
|
|
|
|
$b->color(Graphics::Color::RGB->new(red => 0, green => 0, blue => 0)); |
42
|
|
|
|
|
|
|
$b->width(1); |
43
|
|
|
|
|
|
|
return $b; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
); |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
has 'color_allocator' => ( |
49
|
|
|
|
|
|
|
is => 'rw', |
50
|
|
|
|
|
|
|
isa => 'Chart::Clicker::Drawing::ColorAllocator', |
51
|
|
|
|
|
|
|
default => sub { Chart::Clicker::Drawing::ColorAllocator->new } |
52
|
|
|
|
|
|
|
); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
has 'contexts' => ( |
56
|
|
|
|
|
|
|
traits => [ 'Hash' ], |
57
|
|
|
|
|
|
|
is => 'rw', |
58
|
|
|
|
|
|
|
isa => 'HashRef[Chart::Clicker::Context]', |
59
|
|
|
|
|
|
|
default => sub { { default => Chart::Clicker::Context->new(name => 'default') } }, |
60
|
|
|
|
|
|
|
handles => { |
61
|
|
|
|
|
|
|
'set_context' => 'set', |
62
|
|
|
|
|
|
|
'get_context' => 'get', |
63
|
|
|
|
|
|
|
'context_names' => 'keys', |
64
|
|
|
|
|
|
|
'context_count' => 'count', |
65
|
|
|
|
|
|
|
'delete_context' => 'delete' |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
has '_data' => ( |
70
|
|
|
|
|
|
|
traits => [ 'Hash' ], |
71
|
|
|
|
|
|
|
is => 'rw', |
72
|
|
|
|
|
|
|
isa => 'HashRef[Str]', |
73
|
|
|
|
|
|
|
default => sub { {} } |
74
|
|
|
|
|
|
|
); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
has 'datasets' => ( |
78
|
|
|
|
|
|
|
traits => [ 'Array' ], |
79
|
|
|
|
|
|
|
is => 'rw', |
80
|
|
|
|
|
|
|
isa => 'ArrayRef', |
81
|
|
|
|
|
|
|
default => sub { [] }, |
82
|
|
|
|
|
|
|
handles => { |
83
|
|
|
|
|
|
|
'dataset_count' => 'count', |
84
|
|
|
|
|
|
|
'add_to_datasets' => 'push', |
85
|
|
|
|
|
|
|
'get_dataset' => 'get' |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
has 'driver' => ( |
91
|
|
|
|
|
|
|
is => 'rw', |
92
|
|
|
|
|
|
|
does => 'Graphics::Primitive::Driver', |
93
|
|
|
|
|
|
|
default => sub { |
94
|
|
|
|
|
|
|
my ($self) = @_; |
95
|
|
|
|
|
|
|
my $driver = $ENV{CHART_CLICKER_DEFAULT_DRIVER} || ($^O eq 'MSWin32'?"Graphics::Primitive::Driver::GD":"Graphics::Primitive::Driver::Cairo"); |
96
|
|
|
|
|
|
|
eval "require $driver;" or die "Cannot load driver $driver"; |
97
|
|
|
|
|
|
|
$driver->new( |
98
|
|
|
|
|
|
|
'format' => $self->format, |
99
|
|
|
|
|
|
|
) |
100
|
|
|
|
|
|
|
}, |
101
|
|
|
|
|
|
|
handles => { |
102
|
|
|
|
|
|
|
rendered_data => 'data', |
103
|
|
|
|
|
|
|
write => 'write' |
104
|
|
|
|
|
|
|
}, |
105
|
|
|
|
|
|
|
lazy => 1 |
106
|
|
|
|
|
|
|
); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
has 'format' => ( |
110
|
|
|
|
|
|
|
is => 'rw', |
111
|
|
|
|
|
|
|
isa => 'Str', |
112
|
|
|
|
|
|
|
default => 'PNG' |
113
|
|
|
|
|
|
|
); |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
has 'plot_mode' => ( |
117
|
|
|
|
|
|
|
is => 'rw', |
118
|
|
|
|
|
|
|
isa => 'Str', |
119
|
|
|
|
|
|
|
default => 'slow' |
120
|
|
|
|
|
|
|
); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
has 'grid_over' => ( |
124
|
|
|
|
|
|
|
is => 'rw', |
125
|
|
|
|
|
|
|
isa => 'Bool', |
126
|
|
|
|
|
|
|
default => 0 |
127
|
|
|
|
|
|
|
); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
has '+height' => ( |
131
|
|
|
|
|
|
|
default => 300 |
132
|
|
|
|
|
|
|
); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
has '+layout_manager' => ( |
136
|
|
|
|
|
|
|
default => sub { Layout::Manager::Compass->new } |
137
|
|
|
|
|
|
|
); |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
has 'legend' => ( |
141
|
|
|
|
|
|
|
is => 'rw', |
142
|
|
|
|
|
|
|
isa => 'Chart::Clicker::Decoration::Legend', |
143
|
|
|
|
|
|
|
default => sub { |
144
|
|
|
|
|
|
|
Chart::Clicker::Decoration::Legend->new( |
145
|
|
|
|
|
|
|
name => 'legend', |
146
|
|
|
|
|
|
|
); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
); |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
has 'legend_position' => ( |
152
|
|
|
|
|
|
|
is => 'rw', |
153
|
|
|
|
|
|
|
isa => 'Str', |
154
|
|
|
|
|
|
|
default => 's' |
155
|
|
|
|
|
|
|
); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
has 'marker_overlay' => ( |
159
|
|
|
|
|
|
|
is => 'rw', |
160
|
|
|
|
|
|
|
isa => 'Chart::Clicker::Decoration::MarkerOverlay', |
161
|
|
|
|
|
|
|
lazy => 1, |
162
|
|
|
|
|
|
|
default => sub { |
163
|
|
|
|
|
|
|
Chart::Clicker::Decoration::MarkerOverlay->new |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
); |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
has 'over_decorations' => ( |
169
|
|
|
|
|
|
|
traits => [ 'Array' ], |
170
|
|
|
|
|
|
|
is => 'rw', |
171
|
|
|
|
|
|
|
isa => 'ArrayRef', |
172
|
|
|
|
|
|
|
default => sub { [] }, |
173
|
|
|
|
|
|
|
handles => { |
174
|
|
|
|
|
|
|
'over_decoration_count' => 'count', |
175
|
|
|
|
|
|
|
'add_to_over_decorations' => 'push', |
176
|
|
|
|
|
|
|
'get_over_decoration' => 'get' |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
); |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
has '+padding' => ( |
182
|
|
|
|
|
|
|
default => sub { |
183
|
|
|
|
|
|
|
Graphics::Primitive::Insets->new( |
184
|
|
|
|
|
|
|
top => 3, bottom => 3, right => 3, left => 3 |
185
|
|
|
|
|
|
|
) |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
); |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
has 'plot' => ( |
191
|
|
|
|
|
|
|
is => 'rw', |
192
|
|
|
|
|
|
|
isa => 'Chart::Clicker::Decoration::Plot', |
193
|
|
|
|
|
|
|
default => sub { |
194
|
|
|
|
|
|
|
Chart::Clicker::Decoration::Plot->new |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
has 'subgraphs' => ( |
200
|
|
|
|
|
|
|
is => 'rw', |
201
|
|
|
|
|
|
|
isa => 'ArrayRef', |
202
|
|
|
|
|
|
|
default => sub { [] }, |
203
|
|
|
|
|
|
|
predicate => 'has_subgraphs' |
204
|
|
|
|
|
|
|
); |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
has 'title' => ( |
208
|
|
|
|
|
|
|
is => 'rw', |
209
|
|
|
|
|
|
|
isa => 'Graphics::Primitive::TextBox', |
210
|
|
|
|
|
|
|
default => sub { |
211
|
|
|
|
|
|
|
Graphics::Primitive::TextBox->new( |
212
|
|
|
|
|
|
|
color => Graphics::Color::RGB->new( red => 0, green => 0, blue => 0), |
213
|
|
|
|
|
|
|
horizontal_alignment => 'center' |
214
|
|
|
|
|
|
|
) |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
has 'title_position' => ( |
220
|
|
|
|
|
|
|
is => 'rw', |
221
|
|
|
|
|
|
|
isa => 'Str', |
222
|
|
|
|
|
|
|
default => 'n' |
223
|
|
|
|
|
|
|
); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
has '+width' => ( |
227
|
|
|
|
|
|
|
default => 500 |
228
|
|
|
|
|
|
|
); |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub add_to_contexts { |
232
|
2
|
|
|
2
|
1
|
15
|
my ($self, $ctx) = @_; |
233
|
|
|
|
|
|
|
|
234
|
2
|
50
|
|
|
|
63
|
if(defined($self->get_context($ctx->name))) { |
235
|
0
|
|
|
|
|
0
|
croak("Context named '".$ctx->name."' already exists."); |
236
|
|
|
|
|
|
|
} |
237
|
2
|
|
|
|
|
55
|
$self->set_context($ctx->name, $ctx); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub add_subgraph { |
242
|
0
|
|
|
0
|
1
|
0
|
my ($self, $graph) = @_; |
243
|
|
|
|
|
|
|
|
244
|
0
|
0
|
0
|
|
|
0
|
if (not ref $graph or not $graph->isa('Chart::Clicker')) { |
245
|
0
|
|
|
|
|
0
|
die('Sub-Graphs must be Chart::Clicker objects'); |
246
|
|
|
|
|
|
|
} |
247
|
0
|
|
|
|
|
0
|
push(@{$self->subgraphs}, $graph); |
|
0
|
|
|
|
|
0
|
|
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
sub data { |
251
|
0
|
|
|
0
|
0
|
0
|
my ($self) = @_; |
252
|
0
|
|
|
|
|
0
|
print STDERR "WARNING: Calling 'data' to get image data is deprecated, please use rendered_data\n"; |
253
|
0
|
|
|
|
|
0
|
$self->rendered_data; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
sub draw { |
258
|
6
|
|
|
6
|
1
|
47
|
my ($self) = @_; |
259
|
6
|
|
|
|
|
194
|
my $driver = $self->driver; |
260
|
0
|
|
|
|
|
0
|
$driver->prepare($self); |
261
|
|
|
|
|
|
|
|
262
|
0
|
|
|
|
|
0
|
$self->layout_manager->do_layout($self); |
263
|
0
|
|
|
|
|
0
|
$driver->finalize($self); |
264
|
0
|
|
|
|
|
0
|
$driver->draw($self); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
sub get_datasets_for_context { |
269
|
0
|
|
|
0
|
1
|
0
|
my ($self, $name) = @_; |
270
|
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
0
|
my @dses; |
272
|
0
|
|
|
|
|
0
|
foreach my $ds (@{ $self->datasets }) { |
|
0
|
|
|
|
|
0
|
|
273
|
0
|
0
|
|
|
|
0
|
if($ds->context eq $name) { |
274
|
0
|
|
|
|
|
0
|
push(@dses, $ds); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
0
|
|
|
|
|
0
|
return \@dses; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub add_data { |
283
|
10
|
|
|
10
|
1
|
33
|
my ($self, $name, $data) = @_; |
284
|
|
|
|
|
|
|
|
285
|
10
|
50
|
|
|
|
21
|
if(ref($data) eq 'ARRAY') { |
|
|
50
|
|
|
|
|
|
286
|
|
|
|
|
|
|
croak "Can't add arrayref data after adding hashrefs" |
287
|
0
|
0
|
|
|
|
0
|
if ref($self->_data->{$name}) eq 'HASH'; |
288
|
0
|
0
|
|
|
|
0
|
$self->_data->{$name} = [] unless defined($self->_data->{$name}); |
289
|
0
|
|
|
|
|
0
|
push(@{ $self->_data->{$name}}, @{ $data }); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
290
|
|
|
|
|
|
|
} elsif(ref($data) eq 'HASH') { |
291
|
0
|
0
|
|
|
|
0
|
if (!defined $self->_data->{$name}) { |
|
|
0
|
|
|
|
|
|
292
|
0
|
|
|
|
|
0
|
$self->_data->{$name} = {}; |
293
|
|
|
|
|
|
|
} elsif (ref($self->_data->{$name}) eq 'ARRAY') { |
294
|
0
|
|
|
|
|
0
|
my $old_data = $self->_data->{$name}; |
295
|
0
|
|
|
|
|
0
|
$self->_data->{$name} = {}; |
296
|
0
|
|
|
|
|
0
|
for my $i (0 .. @$old_data - 1) { |
297
|
0
|
|
|
|
|
0
|
$self->_data->{$name}{$i} = $old_data->[$i]; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
} |
300
|
0
|
|
|
|
|
0
|
for my $key (keys %$data) { |
301
|
0
|
|
|
|
|
0
|
$self->_data->{$name}{$key} = $data->{$key}; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
} else { |
304
|
|
|
|
|
|
|
croak "Can't add scalar data after adding hashrefs" |
305
|
10
|
50
|
|
|
|
233
|
if ref($self->_data->{$name}) eq 'HASH'; |
306
|
10
|
100
|
|
|
|
235
|
$self->_data->{$name} = [] unless defined($self->_data->{$name}); |
307
|
10
|
|
|
|
|
9
|
push(@{ $self->_data->{$name}}, $data); |
|
10
|
|
|
|
|
225
|
|
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
override('prepare', sub { |
312
|
|
|
|
|
|
|
my ($self, $driver) = @_; |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
return if $self->prepared; |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
if(scalar(keys(%{ $self->_data }))) { |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
my $ds = Chart::Clicker::Data::DataSet->new; |
319
|
|
|
|
|
|
|
foreach my $name (keys(%{ $self->_data })) { |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
my $vals = $self->_data->{$name}; |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
if(ref($vals) eq 'ARRAY') { |
324
|
|
|
|
|
|
|
# This allows the user to add data as an array |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
$ds->add_to_series( |
327
|
|
|
|
|
|
|
Chart::Clicker::Data::Series->new( |
328
|
|
|
|
|
|
|
name => $name, |
329
|
|
|
|
|
|
|
keys => [ 0..scalar(@{ $vals }) - 1 ], |
330
|
|
|
|
|
|
|
values => $vals |
331
|
|
|
|
|
|
|
) |
332
|
|
|
|
|
|
|
); |
333
|
|
|
|
|
|
|
} elsif(ref($vals) eq 'HASH') { |
334
|
|
|
|
|
|
|
# This allows the user to add data as a hashref |
335
|
|
|
|
|
|
|
my @keys = sort { $a <=> $b } keys %{ $vals }; |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
my @values = (); |
338
|
|
|
|
|
|
|
foreach my $k (@keys) { |
339
|
|
|
|
|
|
|
push(@values, $vals->{$k}) |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
$ds->add_to_series( |
343
|
|
|
|
|
|
|
Chart::Clicker::Data::Series->new( |
344
|
|
|
|
|
|
|
name => $name, |
345
|
|
|
|
|
|
|
keys => \@keys, |
346
|
|
|
|
|
|
|
values => \@values |
347
|
|
|
|
|
|
|
) |
348
|
|
|
|
|
|
|
); |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
$self->add_to_datasets($ds); |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
unless(scalar(@{ $self->components })) { |
355
|
|
|
|
|
|
|
$self->add_component($self->plot, 'c'); |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
my $lp = lc($self->legend_position); |
358
|
|
|
|
|
|
|
if($self->legend->visible) { |
359
|
|
|
|
|
|
|
if(($lp =~ /^e/) || ($lp =~ /^w/)) { |
360
|
|
|
|
|
|
|
$self->legend->orientation('vertical'); |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
$self->add_component($self->legend, $self->legend_position); |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# Add subgraphs |
366
|
|
|
|
|
|
|
if($self->has_subgraphs) { |
367
|
|
|
|
|
|
|
for my $subgraph (@{$self->subgraphs}) { |
368
|
|
|
|
|
|
|
$subgraph->border->width(0); |
369
|
|
|
|
|
|
|
$subgraph->padding(0); |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
$self->add_component($subgraph, 'south'); |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
if(defined($self->title->text)) { |
376
|
|
|
|
|
|
|
my $tp = $self->title_position; |
377
|
|
|
|
|
|
|
if(($tp =~ /^e/) || ($tp =~ /^w/)) { |
378
|
|
|
|
|
|
|
unless(defined($self->title->angle)) { |
379
|
|
|
|
|
|
|
$self->title->angle(-1.5707); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
$self->add_component($self->title, $tp); |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
my $plot = $self->plot; |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
$plot->clear_components; |
389
|
|
|
|
|
|
|
$plot->render_area->clear_components; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# These two adds are here because the plot is too dependant on changes |
392
|
|
|
|
|
|
|
# in the axes and such to trust it across multiple prepares. Putting all |
393
|
|
|
|
|
|
|
# of this here made it easier to digest, although this has some codestink |
394
|
|
|
|
|
|
|
# to it... |
395
|
|
|
|
|
|
|
if($plot->grid->visible && !$self->grid_over) { |
396
|
|
|
|
|
|
|
$plot->render_area->add_component($plot->grid, 'c'); |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
$plot->render_area->add_component( |
400
|
|
|
|
|
|
|
$self->marker_overlay |
401
|
|
|
|
|
|
|
); |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# Sentinels to control the side that the axes will be drawn on. |
404
|
|
|
|
|
|
|
my $dcount = 0; |
405
|
|
|
|
|
|
|
my $rcount = 0; |
406
|
|
|
|
|
|
|
# Hashes of axes & renderers we've already seen, as we don't want to add |
407
|
|
|
|
|
|
|
# them again... |
408
|
|
|
|
|
|
|
my %xaxes; |
409
|
|
|
|
|
|
|
my %yaxes; |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# A "seen" hash to prevent us from adding multiple renderers for the same |
412
|
|
|
|
|
|
|
# context. |
413
|
|
|
|
|
|
|
my %rends; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
my $dflt_ctx = $self->get_context('default'); |
416
|
|
|
|
|
|
|
die('Clicker must have a default context') unless defined($dflt_ctx); |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# Prepare the datasets and establish ranges for the axes. |
419
|
|
|
|
|
|
|
my $count = 0; |
420
|
|
|
|
|
|
|
foreach my $ds (@{ $self->datasets }) { |
421
|
|
|
|
|
|
|
unless($ds->count > 0) { |
422
|
|
|
|
|
|
|
die("Dataset $count is empty."); |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
$ds->prepare; |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
my $ctx = $self->get_context($ds->context); |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
unless(defined($ctx)) { |
430
|
|
|
|
|
|
|
$ctx = $dflt_ctx; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# Find our x axis and add it. |
434
|
|
|
|
|
|
|
my $xaxis = $ctx->domain_axis; |
435
|
|
|
|
|
|
|
unless(exists($xaxes{refaddr($xaxis)})) { |
436
|
|
|
|
|
|
|
$xaxis->range->combine($ds->domain); |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
$xaxis->orientation('horizontal'); |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
if($dcount % 2) { |
441
|
|
|
|
|
|
|
$xaxis->position('top'); |
442
|
|
|
|
|
|
|
$xaxis->border->bottom->width($xaxis->brush->width); |
443
|
|
|
|
|
|
|
} else { |
444
|
|
|
|
|
|
|
$xaxis->position('bottom'); |
445
|
|
|
|
|
|
|
$xaxis->border->top->width($xaxis->brush->width); |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
$xaxis->border->color($xaxis->color); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
$plot->add_component($xaxis, $xaxis->is_top ? 'n' : 's'); |
450
|
|
|
|
|
|
|
$xaxes{refaddr($xaxis)} = 1; |
451
|
|
|
|
|
|
|
$dcount++; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
# Find our y axis and add it. |
455
|
|
|
|
|
|
|
my $yaxis = $ctx->range_axis; |
456
|
|
|
|
|
|
|
unless(exists($yaxes{refaddr($yaxis)})) { |
457
|
|
|
|
|
|
|
$yaxis->range->combine($ds->range); |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
$yaxis->orientation('vertical'); |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
if($rcount % 2) { |
462
|
|
|
|
|
|
|
$yaxis->position('right'); |
463
|
|
|
|
|
|
|
$yaxis->border->left->width($yaxis->brush->width); |
464
|
|
|
|
|
|
|
} else { |
465
|
|
|
|
|
|
|
$yaxis->position('left'); |
466
|
|
|
|
|
|
|
$yaxis->border->right->width($yaxis->brush->width); |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
$yaxis->border->color($yaxis->color); |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
$plot->add_component($yaxis, $yaxis->is_left ? 'w' : 'e'); |
471
|
|
|
|
|
|
|
$rcount++; |
472
|
|
|
|
|
|
|
$yaxes{refaddr($yaxis)} = 1; |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
my $rend = $ctx->renderer; |
476
|
|
|
|
|
|
|
if($rend->additive) { |
477
|
|
|
|
|
|
|
$yaxis->range->upper($ds->largest_value_slice); |
478
|
|
|
|
|
|
|
} else { |
479
|
|
|
|
|
|
|
$yaxis->range->combine($ds->range); |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# Only add this renderer to the chart if we haven't seen it already. |
483
|
|
|
|
|
|
|
unless(exists($rends{$ctx->name})) { |
484
|
|
|
|
|
|
|
$rend->context($ctx->name); |
485
|
|
|
|
|
|
|
$rend->clicker($self); |
486
|
|
|
|
|
|
|
$plot->render_area->add_component($rend, 'c'); |
487
|
|
|
|
|
|
|
$rends{$ctx->name} = $rend; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
$count++; |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
if($plot->grid->visible && $self->grid_over) { |
494
|
|
|
|
|
|
|
$plot->grid->background_color->alpha(0); |
495
|
|
|
|
|
|
|
$plot->render_area->add_component($plot->grid, 'c'); |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
foreach my $c (@{ $self->components }) { |
499
|
|
|
|
|
|
|
$c->clicker($self) if $c->can('clicker'); |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
$plot->add_component($plot->render_area, 'c'); |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
foreach my $oc (@{ $self->over_decorations }) { |
505
|
|
|
|
|
|
|
$plot->render_area->add_component($oc, 'c'); |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
super; |
509
|
|
|
|
|
|
|
}); |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
sub set_renderer { |
513
|
3
|
|
|
3
|
1
|
9
|
my ($self, $renderer, $context) = @_; |
514
|
|
|
|
|
|
|
|
515
|
3
|
100
|
|
|
|
12
|
$context = 'default' unless defined($context); |
516
|
|
|
|
|
|
|
|
517
|
3
|
|
|
|
|
199
|
my $ctx = $self->get_context($context); |
518
|
3
|
100
|
|
|
|
24
|
die("Unknown context: '$context'") unless defined($ctx); |
519
|
|
|
|
|
|
|
|
520
|
2
|
|
|
|
|
145
|
$ctx->renderer($renderer); |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
sub write_output { |
525
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
526
|
|
|
|
|
|
|
|
527
|
0
|
|
|
|
|
|
$self->draw; |
528
|
0
|
|
|
|
|
|
$self->write(@_); |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
532
|
|
|
|
|
|
|
|
533
|
7
|
|
|
7
|
|
54
|
no Moose; |
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
67
|
|
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
1; |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
__END__ |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=pod |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=head1 NAME |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
Chart::Clicker - Powerful, extensible charting. |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=head1 VERSION |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
version 2.90 |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=head1 SYNOPSIS |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
use Chart::Clicker; |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
my $cc = Chart::Clicker->new; |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
my @values = (42, 25, 86, 23, 2, 19, 103, 12, 54, 9); |
556
|
|
|
|
|
|
|
$cc->add_data('Sales', \@values); |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# alternately, you can add data one bit at a time... |
559
|
|
|
|
|
|
|
foreach my $v (@values) { |
560
|
|
|
|
|
|
|
$cc->add_data('Sales', $v); |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# Or, if you want to specify the keys you can use a hashref |
564
|
|
|
|
|
|
|
my $data = { 12 => 123, 13 => 341, 14 => 1241 }; |
565
|
|
|
|
|
|
|
$cc->add_data('Sales', $data); |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
$cc->write_output('foo.png'); |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=head1 DESCRIPTION |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
Chart::Clicker aims to be a powerful, extensible charting package that creates |
572
|
|
|
|
|
|
|
really pretty output. Charts can be saved in png, svg, pdf and postscript |
573
|
|
|
|
|
|
|
format. |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
Clicker leverages the power of Graphics::Primitive to create snazzy graphics |
576
|
|
|
|
|
|
|
without being tied to specific backend. You may want to begin with |
577
|
|
|
|
|
|
|
L<Chart::Clicker::Tutorial>. |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
=head1 EXAMPLES |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
For code examples see the examples repository on GitHub: |
582
|
|
|
|
|
|
|
L<http://github.com/gphat/chart-clicker-examples/> |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=head1 FEATURES |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=head2 Renderers |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
Clicker supports the following renderers: |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=over 4 |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item B<Line> |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/line.png" width="500" height="250" alt="Line Chart" /></p> |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=item B<StackedLine> |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/stacked-line.png" width="500" height="250" alt="Stacked Line Chart" /></p> |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=item B<Bar> |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/bar.png" width="500" height="250" alt="Bar Chart" /></p> |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
=item B<StackedBar> |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/stacked-bar.png" width="500" height="250" alt="Stacked Bar Chart" /></p> |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=item B<Area> |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/area.png" width="500" height="250" alt="Area Chart" /></p> |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=item B<StackedArea> |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/stacked-area.png" width="500" height="250" alt="Stacked Area Chart" /></p> |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=item B<Bubble> |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/bubble.png" width="500" height="250" alt="Bubble Chart" /></p> |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=item B<CandleStick> |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/candlestick.png" width="500" height="250" alt="Candlestick Chart" /></p> |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=item B<Point> |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/point.png" width="500" height="250" alt="Point Chart" /></p> |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=item B<Pie> |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/pie.png" width="300" height="250" alt="Pie Chart" /></p> |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
=item B<PolarArea> |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/polararea.png" width="300" height="250" alt="Polar Area Chart" /></p> |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=back |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=head1 ADDING DATA |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
The synopsis shows the simple way to add data. |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
my @values = (42, 25, 86, 23, 2, 19, 103, 12, 54, 9); |
643
|
|
|
|
|
|
|
foreach my $v (@values) { |
644
|
|
|
|
|
|
|
$cc->add_data('Sales', $v); |
645
|
|
|
|
|
|
|
} |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
This is a convenience method provided to make simple cases much simpler. Adding |
648
|
|
|
|
|
|
|
multiple Series to a chart is as easy as changing the name argument of |
649
|
|
|
|
|
|
|
C<add_data>. Each unique first argument will result in a separate series. See |
650
|
|
|
|
|
|
|
the docs for C<add_data> to learn more. |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
If you'd like to use the more advanced features of Clicker you'll need to |
653
|
|
|
|
|
|
|
shake off this simple method and build Series & DataSets explicitly. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
use Chart::Clicker::Data::Series; |
656
|
|
|
|
|
|
|
use Chart::Clicker::Data::DataSet; |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
... |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
my $series = Chart::Clicker::Data::Series->new( |
661
|
|
|
|
|
|
|
keys => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], |
662
|
|
|
|
|
|
|
values => [ 42, 25, 86, 23, 2, 19, 103, 12, 54, 9 ], |
663
|
|
|
|
|
|
|
); |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
my $ds = Chart::Clicker::Data::DataSet->new(series => [ $series ]); |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
$cc->add_to_datasets($ds); |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
This used to be the only way to add data, but repeated requests to make the |
670
|
|
|
|
|
|
|
common case easier resulted in the inclusion of C<add_data>. |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=head1 CONTEXTS |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
The normal use case for a chart is a couple of datasets on the same axes. |
675
|
|
|
|
|
|
|
Sometimes you want to chart one or more datasets on different axes. A common |
676
|
|
|
|
|
|
|
need for this is when you are comparing two datasets of vastly different scale |
677
|
|
|
|
|
|
|
such as the number of employees in an office (1-10) to monthly revenues (10s |
678
|
|
|
|
|
|
|
of thousands). On a normal chart the number of employees would show up as a |
679
|
|
|
|
|
|
|
flat line at the bottom of the chart. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
To correct this, Clicker has contexts. A context is a pair of axes, a |
682
|
|
|
|
|
|
|
renderer and a name. The name is the 'key' by which you will refer to the |
683
|
|
|
|
|
|
|
context. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
my $context = Chart::Clicker::Context->new( name => 'sales' ); |
686
|
|
|
|
|
|
|
$clicker->add_to_contexts($context); |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
$dataset->context('sales'); |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
$clicker->add_to_datasets($dataset); |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
New contexts provide a fresh domain and range axis and default to a Line |
693
|
|
|
|
|
|
|
renderer. |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
B<Caveat>: Clicker expects that the default context (identified by the string |
696
|
|
|
|
|
|
|
"default") will always be present. It is from this context that some of |
697
|
|
|
|
|
|
|
Clicker's internals draw their values. You should use the default context |
698
|
|
|
|
|
|
|
unless you need more than one, in which case you should use "default" as the |
699
|
|
|
|
|
|
|
base context. |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
=head1 FORMATS & OUTPUT |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
Clicker supports PNG, SVG, PDF and PostScript output. To change your output |
704
|
|
|
|
|
|
|
type, specificy it when you create your Clicker object: |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
my $cc = Chart::Clicker->new(format => 'pdf', ...); |
707
|
|
|
|
|
|
|
# ... |
708
|
|
|
|
|
|
|
$cc->write_output('chart.pdf'); |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
If you are looking to get a scalar of the output for use with HTTP or |
711
|
|
|
|
|
|
|
similar things, you can use: |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# ... make your chart |
714
|
|
|
|
|
|
|
$cc->draw; |
715
|
|
|
|
|
|
|
my $image_data = $cc->rendered_data; |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
If you happen to be using Catalyst then take a look at |
718
|
|
|
|
|
|
|
L<Catalyst::View::Graphics::Primitive>. |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=head2 background_color |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
Set/Get the background L<color|Graphics::Color::RGB>. Defaults to white. |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=head2 border |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Set/Get the L<border|Graphics::Primitive::Border>. |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=head2 color_allocator |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
Set/Get the L<color_allocator|Chart::Clicker::Drawing::ColorAllocator> for this chart. |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=head2 contexts |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
Set/Get the L<contexts|Chart::Clicker::Context> for this chart. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=head2 datasets |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Get/Set the datasets for this chart. |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=head2 driver |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
Set/Get the L<driver|Graphics::Primitive::Driver> used to render this Chart. Defaults to |
745
|
|
|
|
|
|
|
L<Graphics::Primitive::Driver::Cairo>. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
=head2 format |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
Get the format for this Chart. Required in the constructor. Must be on of |
750
|
|
|
|
|
|
|
Png, Pdf, Ps or Svg. |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=head2 plot_mode |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
Fast or slow plot mode. When in fast mode, data elements that are deemed to be |
755
|
|
|
|
|
|
|
superfluous or invisible will not be drawn. Default is 'slow' |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=head2 grid_over |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
Flag controlling if the grid is rendered B<over> the data. Defaults to 0. |
760
|
|
|
|
|
|
|
You probably want to set the grid's background color to an alpha of 0 if you |
761
|
|
|
|
|
|
|
enable this flag. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=head2 height |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
Set/Get the height. Defaults to 300. |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
=head2 layout_manager |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
Set/Get the layout manager. Defaults to L<Layout::Manager::Compass>. |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
=head2 legend |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
Set/Get the L<legend|Chart::Clicker::Decoration::Legend> that will be used with this chart. |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
=head2 legend_position |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
The position the legend will be added. Should be one of north, south, east, |
778
|
|
|
|
|
|
|
west or center as required by L<Layout::Manager::Compass>. |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=head2 marker_overlay |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
Set/Get the L<marker overlay|Chart::Clicker::Decoration::MarkerOverlay> object that will be used if this chart |
783
|
|
|
|
|
|
|
has markers. This is lazily constructed to save time. |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=head2 over_decorations |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
Set/Get an arrayref of "over decorations", or things that are drawn OVER the |
788
|
|
|
|
|
|
|
chart. This is an advanced feature. See C<overaxis-bar.pl> in the examples. |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=head2 padding |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
Set/Get the L<padding|Graphics::Primitive::Insets>. Defaults |
793
|
|
|
|
|
|
|
to 3px on all sides. |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=head2 plot |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
Set/Get the L<plot|Chart::Clicker::Decoration::Plot> on which things are drawn. |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=head2 subgraphs |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
You can add "child" graphs to this one via C<add_subgraph>. These must be |
802
|
|
|
|
|
|
|
Chart::Clicker objects and they will be added to the bottom of the existing |
803
|
|
|
|
|
|
|
chart. This is a rather esoteric feature. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
=head2 title |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
Set/Get the title component for this chart. This is a |
808
|
|
|
|
|
|
|
L<Graphics::Primitive::TextBox>, not a string. To set the title of a chart |
809
|
|
|
|
|
|
|
you should access the TextBox's C<text> method. |
810
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
$cc->title->text('A Title!'); |
812
|
|
|
|
|
|
|
$cc->title->font->size(20); |
813
|
|
|
|
|
|
|
# etc, etc |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
If the title has text then it is added to the chart in the position specified |
816
|
|
|
|
|
|
|
by C<title_position>. |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
You should consult the documentation for L<Graphics::Primitive::TextBox> for |
819
|
|
|
|
|
|
|
things like padding and text rotation. If you are adding it to the top and |
820
|
|
|
|
|
|
|
want some padding between it and the plot, you can: |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
$cc->title->padding->bottom(5); |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
=head2 title_position |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
The position the title will be added. Should be one of north, south, east, |
827
|
|
|
|
|
|
|
west or center as required by L<Layout::Manager::Compass>. |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
Note that if no angle is set for the title then it will be changed to |
830
|
|
|
|
|
|
|
-1.5707 if the title position is east or west. |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
=head2 width |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
Set/Get the width. Defaults to 500. |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=head1 METHODS |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=head2 context_count |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Get a count of contexts. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=head2 context_names |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
Get a list of context names. |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=head2 delete_context ($name) |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
Remove the context with the specified name. |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=head2 get_context ($name) |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
Get the context with the specified name |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
=head2 set_context ($name, $context) |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
Set a context of the specified name. |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
=head2 add_to_datasets |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
Add the specified dataset (or arrayref of datasets) to the chart. |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=head2 dataset_count |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
Get a count of datasets. |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=head2 get_dataset ($index) |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
Get the dataset at the specified index. |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=head2 rendered_data |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
Returns the data for this chart as a scalar. Suitable for 'streaming' to a |
873
|
|
|
|
|
|
|
client. |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
=head2 add_to_over_decorations |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
Add an over decoration to the list. |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
=head2 get_over_decoration ($index) |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
Get the over decoration at the specified index. |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
=head2 over_decoration_count |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
Get a count of over decorations. |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
=head2 add_to_contexts |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
Add the specified context to the chart. |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
=head2 add_subgraph |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
Add a subgraph to this chart. |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=head2 draw |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
Draw this chart. |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
=head2 get_datasets_for_context |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
Returns an arrayref containing all datasets for the given context. Used by |
902
|
|
|
|
|
|
|
renderers to get a list of datasets to chart. |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=head2 add_data ($name, $data) |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
Convenience method for adding data to the chart. Can be called one of three |
907
|
|
|
|
|
|
|
ways. |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
=over 4 |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=item B<scalar> |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
Passing a name and a scalar will append the scalar data to that series' data. |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
$cc->add_data('Sales', 1234); |
916
|
|
|
|
|
|
|
$cc->add_data('Sales', 1235); |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
This will result in a Series named 'Sales' with two values. |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
=item B<arrayref> |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
Passing a name and an arrayref works much the same as the scalar method |
923
|
|
|
|
|
|
|
discussed above, but appends the supplied arrayref to the existing one. It |
924
|
|
|
|
|
|
|
may be mixed with the scalar method. |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
$cc->add_data('Sales', \@some_sales); |
927
|
|
|
|
|
|
|
$cc->add_data('Sales', \@some_more_sales); |
928
|
|
|
|
|
|
|
# This works still! |
929
|
|
|
|
|
|
|
$cc->add_data('Sales', 1234); |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
=item B<hashref> |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
This allows you to pass both keys and values in all at once. |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
$cc->add_data('Sales', { 2009 => 1234, 2010 => 1235 }); |
936
|
|
|
|
|
|
|
# appends to last call |
937
|
|
|
|
|
|
|
$cc->add_data('Sales', { 2011 => 1234, 2012 => 1235 }); |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
You may call the hashref version after the scalar or arrayref versions, but you |
940
|
|
|
|
|
|
|
may not add a scalar or arrayref after adding a hashref (as it's not clear what |
941
|
|
|
|
|
|
|
indices should be used for the new data). |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
=back |
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
=head2 set_renderer ($renderer_object, [ $context ]); |
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
Sets the renderer on the specified context. If no context is provided then |
948
|
|
|
|
|
|
|
'default' is assumed. |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
=head2 write |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
This method is passed through to the underlying driver. It is only necessary |
953
|
|
|
|
|
|
|
that you call this if you manually called C<draw> beforehand. You likely |
954
|
|
|
|
|
|
|
want to use C<write_output>. |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=head2 write_output ($path) |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
Write the chart output to the specified location. Output is written in the |
959
|
|
|
|
|
|
|
format provided to the constructor (which defaults to Png). Internally |
960
|
|
|
|
|
|
|
calls C<draw> for you. If you use this method, do not call C<draw> first! |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
$c->write_output('/path/to/the.png'); |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
=head2 inside_width |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
Get the width available in this container after taking away space for |
967
|
|
|
|
|
|
|
insets and borders. |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
=head2 inside_height |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
Get the height available in this container after taking away space for |
972
|
|
|
|
|
|
|
insets and borders. |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
=head1 ISSUES WITH CENTOS |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
I've had numerous reports of problems with Chart::Clicker when using CentOS. |
977
|
|
|
|
|
|
|
This problem has usually be solved by updating the version of cairo. I've |
978
|
|
|
|
|
|
|
had reports that upgrading to at least cairo-1.8.8-3 makes thinks work properly. |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
I hesitate to provide any other data with this because it may get out of date |
981
|
|
|
|
|
|
|
fast. If you have trouble feel free to drop me an email and I'll tell you |
982
|
|
|
|
|
|
|
what I know. |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
Many thanks to the individuals who have contributed various bits: |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
Ash Berlin |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
Brian Cassidy |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
Guillermo Roditi |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
Torsten Schoenfeld |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
Yuval Kogman |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
=head1 SOURCE |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
Chart::Clicker is on github: |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
http://github.com/gphat/chart-clicker/tree/master |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
=head1 AUTHOR |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
Cory G Watson <gphat@cpan.org> |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
This software is copyright (c) 2016 by Cory G Watson. |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
1013
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
=cut |