File Coverage

blib/lib/Tk/Graph.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             package Tk::Graph;
2             #------------------------------------------------
3             # automagically updated versioning variables -- CVS modifies these!
4             #------------------------------------------------
5             our $Revision = '$Revision: 1.58 $';
6             our $CheckinDate = '$Date: 2002/12/12 16:04:55 $';
7             our $CheckinUser = '$Author: xpix $';
8             # we need to clean these up right here
9             $Revision =~ s/^\$\S+:\s*(.*?)\s*\$$/$1/sx;
10             $CheckinDate =~ s/^\$\S+:\s*(.*?)\s*\$$/$1/sx;
11             $CheckinUser =~ s/^\$\S+:\s*(.*?)\s*\$$/$1/sx;
12             #-------------------------------------------------
13             #-- package Tk::Graph ----------------------------
14             #-------------------------------------------------
15            
16             =head1 NAME
17            
18             Tk::Graph - A graphical Chartmaker at Canvas (Realtime).
19            
20             =head1 SYNOPSIS
21            
22             use Tk;
23             use Tk::Graph;
24            
25             $mw = MainWindow->new;
26            
27             my $data = {
28             Sleep => 51,
29             Work => 135,
30             Access => 124,
31             mySQL => 5
32             };
33            
34             my $ca = $mw->Graph(
35             -type => 'BARS',
36             )->pack(
37             -expand => 1,
38             -fill => 'both',
39             );
40            
41             $ca->configure(-variable => $data); # bind to data
42            
43             # or ...
44            
45             $ca->set($data); # set data
46            
47             MainLoop;
48            
49            
50             =head1 DESCRIPTION
51            
52             A graphical Chartmaker at Canvas (Realtime). This is a real Canvas widget,
53             so you can draw with the standard routines in the Canvas object.
54             For example, you can draw a line with I<$chart>->I. This is useful for you when you will
55             add a logo or write some text in your created Chart.
56            
57             =cut
58            
59            
60             # -------------------------------------------------------
61             #
62             # Graph.pm
63             #
64             # A graphical Chartmaker at Canvas (Realtime)
65             # -------------------------------------------------------
66            
67 1     1   1109 use Carp;
  1         2  
  1         90  
68 1     1   6 use base qw/Tk::Derived Tk::Canvas/;
  1         1  
  1         1552  
69             use Math::Trig qw(rad2deg acos);
70             use Tie::Watch;
71             use strict;
72            
73             Construct Tk::Widget 'Graph';
74            
75             #-------------------------------------------------
76             sub Populate {
77             #-------------------------------------------------
78             my ($self, $args) = @_;
79             $self->SUPER::Populate($args);
80            
81             =head1 WIDGET-SPECIFIC OPTIONS
82            
83             =cut
84            
85             my %specs;
86            
87             #-------------------------------------------------
88             $specs{-debug} = [qw/PASSIVE debug Debug/, undef];
89            
90             =head2 -debug [I<0>|1]
91            
92             This is the switch for debug output at the normal console (STDOUT)
93            
94             =cut
95            
96             #-------------------------------------------------
97             $specs{-type} = [qw/PASSIVE type Type/, undef];
98            
99             =head2 -type (I, Line, Bars, HBars, Circle)
100            
101             This is the type of Graph to display the data.
102            
103             I - analyze the datahash and choose a Chart:
104            
105             Hash with values -> PieChart
106             Hash with keys with hashes or values (not all) -> Barchart per Key
107             Hash with keys with arrays -> Linechart per Key
108             Array -> Linechart
109            
110             I - Linechart,
111            
112             I - Barchart with vertical Bars,
113            
114             I - Barchart with horizontal bars,
115            
116             I - PieChart
117            
118             =cut
119            
120             #-------------------------------------------------
121             $specs{-foreground} = [qw/PASSIVE foreground Foreground/, 'black'];
122            
123             =head2 -foreground (I)
124            
125             Color for the Axis, Legend and Labels.
126            
127             =cut
128            
129             #-------------------------------------------------
130             $specs{-titlecolor} = [qw/PASSIVE titlecolor TitleColor brown/];
131             $specs{-title} = [qw/PASSIVE title Title/, ' '];
132            
133             =head2 -title -titlecolor (I)
134            
135             Message at the top of the Widget.
136            
137             =cut
138            
139             #-------------------------------------------------
140             $specs{-headroom} = [qw/PASSIVE headroom HeadRoom/, 20];
141            
142             =head2 -headroom (I<20>)
143            
144             The headroom in percent. This is a clean area at the top of the widget.
145             When a value is in this area, the graph is redrawn to preserve this headroom.
146            
147             =cut
148            
149             #-------------------------------------------------
150             $specs{-threed} = [qw/PASSIVE threed Threed/, undef];
151            
152             =head2 -threed (I)
153            
154             This switch a three dimensional Display on. The Value is deep in Pixel.
155            
156             =cut
157            
158             #-------------------------------------------------
159             $specs{-light} = [qw/PASSIVE light Light/, [10,5,0]];
160            
161             =head2 -light (I<[10,5,0]>)
162            
163             How many percent is the color in top, side and front (in this direction)
164             lighter or darker in 3d?
165            
166             =cut
167            
168            
169             #-------------------------------------------------
170             $specs{-max} = [qw/PASSIVE max Max/, undef];
171            
172             =head2 -max
173            
174             Maximum Value for the axis. If this set,
175             the axis is not dynamically redrawn to the
176             next maximum value from the data.
177             Only used in Lines and Bars!
178            
179             =cut
180            
181             #-------------------------------------------------
182             $specs{-sortnames} = [qw/PASSIVE sortnames SortNames/, 'alpha'];
183             $specs{-sortreverse} = [qw/PASSIVE sortreverse SortReverse/, undef];
184            
185             =head2 -sortnames ('I' | 'num') -sortreverse (0, 1)
186            
187             sort the keys from the data hash.
188            
189             =cut
190            
191             #-------------------------------------------------
192             $specs{-config} = [qw/PASSIVE config Config/, undef];
193            
194             =head2 -config (\%cfghash)
195            
196             A config hash with optional added parameters for more flexibility. The first is the name
197             of the key from your data hash, followed by a config hash with parameters.
198             example:
199            
200             -config => {
201             'fr' => {
202             -title => 'Free',
203             -color => 'green',
204             -range => {
205             'red' => [0, 50],
206             'yellow'=> [50, 100],
207             'green' => [100, 200],
208             },
209             },
210             'sl' => {
211             -title => 'Sleep',
212             -color => 'yellow',
213             },
214             ...
215             },
216            
217             I<-title>
218            
219             Here you can write another Name to display.
220            
221             I<-color>
222            
223             Key name displayed in this color.
224            
225             I<-range>
226            
227             A range to display the values in variable colors. You can say values from 0 to 50 display in
228             green or above in red. if value not in something range, then this draw in original color.
229             This is only use in LINE (ToDo: BARS and HBARS!)
230            
231            
232             =cut
233            
234             #-------------------------------------------------
235             $specs{-fill} = [qw/PASSIVE fill Fill/, 'both'];
236            
237             =head2 -fill (I<'both'>)
238            
239             The same as in perl/tk pack. Redraw only in
240             I,I or I direction(s).
241            
242             =cut
243            
244             #-------------------------------------------------
245             $specs{-ylabel} = [qw/PASSIVE ylabel YLabel/, undef];
246             $specs{-xlabel} = [qw/PASSIVE xlabel XLabel/, undef];
247            
248             =head2 -xlabel -ylabel (I)
249            
250             This displays a description for x and y axis.
251            
252             =cut
253            
254             #-------------------------------------------------
255             $specs{-ytick} = [qw/PASSIVE ytick YTick/, 5];
256             $specs{-xtick} = [qw/PASSIVE xtick XTick/, 5];
257            
258             =head2 -xtick -ytick (I<5>)
259            
260             Number of ticks at the x or y axis.
261            
262             =cut
263            
264             #-------------------------------------------------
265             $specs{-yformat} = [qw/PASSIVE yformat YFormat/, '%g'];
266             $specs{-xformat} = [qw/PASSIVE xformat XFormat/, '%s'];
267            
268             =head2 -xformat (I<'%s'>) -yformat (I<'%g'>)
269            
270             This if the sprintf format for display
271             of the value or key for the axis.
272             example:
273            
274             -xformat => '%d%%' # This will eg. Display '50%'
275             -yformat => '%s host' # This will eg. Display 'first host'
276            
277             =cut
278            
279             #-------------------------------------------------
280             $specs{-padding} = [qw/PASSIVE padding Padding/, [15,20,20,50]];
281            
282             =head2 -padding (I<[15,20,20,50]>)
283            
284             Margin display from the Widget border, in this order top, right, bottom,
285             left.
286            
287             =cut
288            
289             #-------------------------------------------------
290             $specs{-linewidth} = [qw/PASSIVE linewidth Linewidth 1/];
291            
292             =head2 -linewidth (I<1>)
293            
294             The weight of the border for the dots, circle and lines.
295            
296             =cut
297            
298            
299             #-------------------------------------------------
300             $specs{-printvalue} = [qw/PASSIVE printvalue Printvalue/, undef];
301            
302             =head2 -printvalue
303            
304             This is the sprintf format and switch for display of the value.
305            
306             =cut
307            
308             #-------------------------------------------------
309             $specs{-maxmin} = [qw/PASSIVE maxmin MaxMin/, undef];
310            
311             =head2 -maxmin
312            
313             Draw max/average/min value lines in Bars and Line charts
314            
315             =cut
316            
317             #-------------------------------------------------
318             $specs{-legend} = [qw/PASSIVE legend Legend/, 1];
319            
320             =head2 -legend [0|I<1>]
321            
322             Switch on/off the legend in Circle or Lines
323            
324             =cut
325            
326             #-------------------------------------------------
327             $specs{-colors} = [qw/PASSIVE colors Colors/, 'blue,brown,seashell3,red,green,yellow,darkgreen,darkblue,darkred,orange,olivedrab,magenta,black,salmon'];
328            
329             =head2 -colors (I)
330            
331             A comma-separated list with the allowed colors.
332            
333            
334             =cut
335            
336             #-------------------------------------------------
337             $specs{-shadow} = [qw/PASSIVE shadow Shadow/, 'gray50'];
338             $specs{-shadowdepth} = [qw/PASSIVE shadowdepth Shadowdepth/, undef];
339            
340             =head2 -shadow (I<'gray50'>) -shadowdepth (I<0>)
341            
342             You can add a shadow to all Charts, the
343             switch is -shadowdepth. This is also the depth in Pixels for the shadow.
344             -shadow is the color for the shadow. This Option is autoaticly switch off when use 3d.
345            
346             =cut
347            
348             #-------------------------------------------------
349             $specs{-wire} = [qw/PASSIVE wire Wire/, 'white'];
350            
351             =head2 -wire (I<'white'>)
352            
353             Switch on/off a wire grid in background from line and bars chart.
354            
355             =cut
356            
357             #-------------------------------------------------
358             $specs{-reference} = [qw/PASSIVE reference Reference/, undef];
359            
360             =head2 -reference (I<'name'>, I<'value'>)
361            
362             This give a Reference value for the keys in datahash. I.e. the data values are displayed relative to this reference value.
363            
364             example:
365            
366             -reference => 'Free, 1024', # Free space at host
367            
368             =cut
369            
370             #-------------------------------------------------
371             $specs{-look} = [qw/PASSIVE look Look/, 10];
372            
373             =head2 -look (I<10>)
374            
375             The number of values to display in a line chart.
376             When you refresh the data hash (maybe with the methods set or variable), then this will display
377             eg. the last 50 values.
378            
379             example:
380            
381             -look => 50, # 50 values to display pro key
382            
383             =cut
384            
385             #-------------------------------------------------
386             $specs{-dots} = [qw/PASSIVE dots Dots/, undef];
387            
388             =head2 -dots (I<'width'>)
389            
390             The width and switch for the dots in line chart.
391            
392             =cut
393            
394             #-------------------------------------------------
395             $specs{-barwidth} = [qw/PASSIVE barwidth Barwidth/, 30];
396            
397             =head2 -barwidth (I<30>)
398            
399             The width for bars in bar charts.
400            
401             =cut
402            
403             #-------------------------------------------------
404             $specs{-balloon} = [qw/PASSIVE balloon Balloon/, 1];
405            
406             =head2 -balloon (0|I<1>)
407            
408             Switch on/off ballon help for segements or lines.
409             The text format is used from the -printvalue option.
410            
411             =cut
412            
413             #-------------------------------------------------
414             $specs{-font} = [qw/PASSIVE font Font/, '-*-Helvetica-Medium-R-Normal--*-100-*-*-*-*-*-*'];
415            
416             =head2 -font (I<'-*-Helvetica-Medium-R-Normal--*-100-*-*-*-*-*-*'>)
417            
418             Draw text in this font.
419            
420             =cut
421            
422             #-------------------------------------------------
423             $specs{-lineheight} = [qw/PASSIVE lineheight LineHeight/, 15];
424            
425             =head2 -lineheight (I<15>)
426            
427             The line height in pixels for text in the legend.
428            
429             =cut
430            
431             #-------------------------------------------------
432            
433            
434             =head1 METHODS
435            
436             Here come the methods that you can use with this Widget.
437            
438             =cut
439            
440            
441             #-------------------------------------------------
442            
443             #-------------------------------------------------
444             $specs{-set} = [qw/METHOD set Set/, undef];
445            
446             =head2 $chart->I($data);
447            
448             Set the data hash to display.
449            
450             =cut
451            
452             #-------------------------------------------------
453             $specs{-variable} = [qw/METHOD variable Variable/, undef];
454            
455             =head2 $chart->I($data);
456            
457             Bind the data hash to display the data, write to $data will redraw the widget.
458            
459             =cut
460            
461             #-------------------------------------------------
462             $specs{-register} = [qw/METHOD register Register/, undef];
463            
464             =head2 $chart->I($to_register);
465            
466             Set the data hash to register. When you have data for Linegraph
467             then can you this register with method register. This data
468             is the registered for the following linegraph, when you set
469             new datas with 'set' or 'variable' then if this startet at
470             the end from this data. if you call register without data the
471             you get the actual datacache.
472            
473             my $to_register = {
474             'one' => [0,5,4,8,6,8],
475             'two' => [2,5,9,4,6,2],
476             'three' => [0,5,6,8,6,8],
477             };
478             $ca->register($to_register);
479            
480             =cut
481            
482            
483             #-------------------------------------------------
484             $specs{-redraw} = [qw/METHOD redraw Redraw/, undef];
485            
486             =head2 $chart->I();
487            
488             Redraw chart
489            
490             =cut
491            
492             #-------------------------------------------------
493             $specs{-clear} = [qw/METHOD clear Clear/, undef];
494            
495             =head2 $chart->I();
496            
497             Clear the canvas.
498            
499             =cut
500            
501            
502             $self->ConfigSpecs(
503             %specs,
504             );
505            
506             # Bindings
507             $self->Tk::bind('', [ \&redraw, $self ] ); # Redraw
508            
509             # Help (CanvasBalloon)
510             # $self->{balloon} = $self->Balloon;
511            
512             } # end Populate
513            
514             #-------------------------------------------------
515             sub draw_horizontal_bars {
516             #-------------------------------------------------
517             my $self = shift || return error("No Objekt!");
518             return undef unless(ref $self eq __PACKAGE__);
519             my $data = shift || return;
520            
521             # Check
522             return warn("Your data is incorrect, i need a Hashreference!")
523             unless(ref $data eq 'HASH');
524            
525            
526             my $werte = $self->reference($data);
527             my $conf = $self->ReadConfig($werte) || return;
528            
529             $self->delete('all');
530            
531             # MaxMin Werte ermitteln und ggf Linien zeichnen
532             $self->maxmin($conf, $werte);
533            
534             # Gitter zeichnen
535             $self->wire($conf)
536             if( $self->cget(-wire) );
537            
538             # Axis (Titel ... usw
539             $self->axis($conf, $werte);
540            
541            
542             $self->debug("Count: %d,Typ: %s, Max: %s", $conf->{count}, $conf->{typ}, $conf->{max_value});
543             if($conf->{count} > 0 && $conf->{typ} eq 'HASH' && $conf->{max_value} > 0)
544             {
545             my $i = -0.5;
546             my @linepoints;
547             my $c;
548             my $shadowcolor = $self->cget(-shadow);
549             my $sd = $self->cget(-shadowdepth);
550             my $td = $self->cget(-threed) || 0;
551            
552             foreach my $point (sort { $self->sorter } keys %$werte ) {
553             next if(ref $werte->{$point});
554             next unless($conf->{max_value});
555             $i++;
556            
557             my $xi = ($conf->{x_null} + round( ( ($conf->{width} - $conf->{x_null}) / $conf->{max_value} ) * $werte->{$point}));
558             my $yi = ($conf->{y_null}) - (round(($conf->{y_null} - $conf->{ypad}) / $conf->{count}) * $i);
559             $yi-=($self->cget(-barwidth) / 2);
560            
561             # Values
562             $self->createText($xi+12, $yi + ($self->cget(-barwidth) / 2),
563             -text => sprintf($self->cget(-printvalue), '', $werte->{$point}),
564             -anchor => 'w',
565             -font => $conf->{font},
566             -fill => $self->cget(-titlecolor)
567             ) if($self->cget(-printvalue));
568            
569            
570             # Shadow Bar
571             if($sd && $werte->{$point} && ! $td) {
572             my $bar = $self->createRectangle(
573             ($xi+$sd), ($yi+$sd),
574             ($conf->{x_null}), ($yi + $self->cget(-barwidth) + $sd),
575             -fill => $shadowcolor,
576             -outline => $shadowcolor,
577             );
578             }
579            
580             # ThreeD Bar
581             # Oben
582             $self->createPolygon(
583             $conf->{x_null}, $yi,
584             ($conf->{x_null} + $td), ($yi - $td),
585             ($xi + $td), ($yi - $td),
586             ($xi), ($yi ),
587             -fill => $self->color_change( $self->{colors}->{$point}, $conf->{light_top}),
588             -outline => 'black',
589             ) if($td);
590            
591             # Side
592             $self->createPolygon(
593             ($xi ), ($yi + $self->cget(-barwidth)) ,
594             ($xi + $td ), ($yi + $self->cget(-barwidth) - $td) ,
595             ($xi + $td), ($yi - $td),
596             ($xi ), ($yi ),
597             -fill => $self->color_change( $self->{colors}->{$point}, $conf->{light_side}),
598             -outline => 'black',
599             ) if($td);
600            
601             # Normaler Bar
602             $self->{elements}->{$point} = $self->createRectangle($xi, $yi,
603             $conf->{x_null}, ($yi + $self->cget(-barwidth)),
604             -fill => ( $self->cget(-threed) ? $self->color_change( $self->{colors}->{$point}, $conf->{light_front}) : $self->{colors}->{$point} ),
605             -width => 1,
606             ) if($werte->{$point});
607             }
608            
609             # balloon
610             $self->balloon($self->{elements}, $werte);
611            
612             }
613             }
614            
615            
616             #-------------------------------------------------
617             sub draw_bars {
618             #-------------------------------------------------
619             my $self = shift || return error("No Objekt!");
620             return undef unless(ref $self eq __PACKAGE__);
621             my $data = shift || return;
622            
623             # Check
624             return warn("Your data is incorrect, i need a Hashreference!")
625             unless(ref $data eq 'HASH');
626            
627            
628             my $werte = $self->reference($data);
629             my $conf = $self->ReadConfig($werte) || return;
630            
631             $self->delete('all');
632            
633             # MaxMin Werte ermitteln und ggf Linien zeichnen
634             $self->maxmin($conf, $werte);
635            
636             # Gitter zeichnen
637             $self->wire($conf)
638             if( $self->cget(-wire) );
639            
640             # Axis (Titel ... usw
641             $self->axis($conf, $werte);
642            
643            
644            
645             if($conf->{count} > 0 && $conf->{typ} eq 'HASH')
646             {
647             my $i = 0;
648             my ($xi, $yi);
649             my @linepoints;
650             my $c;
651             my $td = $self->cget(-threed) || 0;
652            
653             foreach my $point (sort { $self->sorter } keys %$werte ) {
654             next if(ref $werte->{$point});
655             next unless($conf->{max_value});
656             $werte->{$point} = 0
657             unless(defined $werte->{$point});
658             $i++;
659            
660             $xi = $self->calc_x($i) - ($self->cget(-barwidth) / 2);
661             $yi = $self->calc_y($werte->{$point});
662            
663             $self->debug("---------------------");
664             $self->debug("DrawBar: Name: %s, Wert: %d", $point, $werte->{$point});
665            
666             $self->bar( $point, $werte->{$point}, $i );
667             }
668             } else {
669             return $self->error("I need a hash to display Bars!");
670             }
671            
672             # balloon
673             $self->balloon($self->{elements}, $werte);
674             }
675            
676             #-------------------------------------------------
677             sub bar {
678             #-------------------------------------------------
679             my $self = shift || return error("No Objekt!");
680             return undef unless(ref $self eq __PACKAGE__);
681             my $name = shift || return error('No Name!');
682             my $wert = shift || 0;
683             my $i = shift;
684            
685             my $conf = $self->{cfg};
686             my $xi = $self->calc_x($i) - round($self->cget(-barwidth) / 2);
687             my $yi = $self->calc_y($wert);
688             my $width = $self->cget(-barwidth);
689             my $height = $conf->{y_null};
690             my $td = $self->cget(-threed) || 0;
691            
692             $self->debug('Name: %s, X: %d, Y:%d, Width: %d, Hight: %d, 3D: %d',
693             $name, $xi, $yi, $width, $height, $td);
694            
695            
696             # ThreeD Bar
697             # -----------------------------------
698             if( $td ) {
699             # Oben
700             $self->createPolygon(
701             $xi, $yi,
702             ($xi + $td), ($yi - $td),
703             ($xi + $width + $td), ($yi - $td),
704             ($xi + $width ), ($yi ),
705             -fill => $self->color_change( $self->{colors}->{$name}, $conf->{light_top}),
706             -outline => 'black',
707             );
708             # Side
709             $self->createPolygon(
710             ($xi + $width ), $conf->{y_null} ,
711             ($xi + $width+$td ), ($conf->{y_null} - $td) ,
712             ($xi + $width+$td), ($yi - $td),
713             ($xi + $width ), ($yi ),
714             -fill => $self->color_change( $self->{colors}->{$name}, $conf->{light_side}),
715             -outline => 'black',
716             );
717             }
718            
719             # Shadow Bar
720             if($wert && $self->cget(-shadowdepth) && (my $shadowcolor = $self->cget(-shadow)) && (my $sd = $self->cget(-shadowdepth)) && ! $self->cget(-threed)) {
721             $self->createRectangle(
722             ($xi+$sd), ($yi+$sd),
723             ($xi + $self->cget(-barwidth)+$sd), $conf->{y_null},
724             -fill => $shadowcolor,
725             -outline => $shadowcolor,
726             );
727             }
728            
729             # Normaler Bar
730             $self->{elements}->{$name} = $self->createRectangle(
731             $xi, $yi,
732             ($xi + $width), $height,
733             -fill => ( $self->cget(-threed) ? $self->color_change( $self->{colors}->{$name}, $conf->{light_front}) : $self->{colors}->{$name} ),
734             -width => 1,
735             ) if($wert);
736             # -----------------------------------
737            
738             # Values
739             $self->createText($xi+12+$td, $yi-12-$td,
740             -text => sprintf($self->cget(-printvalue), '', $wert),
741             -anchor => 'n',
742             -font => $conf->{font},
743             -fill => $self->cget(-titlecolor)
744             ) if($self->cget(-printvalue));
745            
746            
747            
748            
749             }
750            
751             #-------------------------------------------------
752             sub color_change {
753             #-------------------------------------------------
754             my $self = shift || return error("No Objekt!");
755             return undef unless(ref $self eq __PACKAGE__);
756             my $col = shift || return error("No Color!");
757             my $fac = shift || return $col;
758            
759             my @colors = $self->rgb($col);
760             my $wert = '#';
761             foreach (@colors) {
762             my $dec = $_;
763             my $w = ($dec + (($dec * $fac) / 100));
764             $w = 0xFFFF if($w > 0xFFFF);
765             $w = 0 if($w < 0);
766             $wert .= sprintf('%X', $w);
767             }
768            
769             $self->debug(
770             'Col: %s, Fac: %s, ColAfter: %s',
771             $col, $fac, $wert);
772            
773             return $wert;
774             }
775            
776             #-------------------------------------------------
777             sub draw_line {
778             #-------------------------------------------------
779             my $self = shift || return error("No Objekt!");
780             return undef unless(ref $self eq __PACKAGE__);
781             my $data = shift || return;
782             my $werte = $self->reference($data);
783             my $conf = $self->ReadConfig($werte) || return;
784             my $MAX;
785            
786             $self->delete('all');
787            
788             # Zeitverfolgung
789             $self->look($werte) if($data);
790            
791             # MaxMin Werte ermitteln und ggf. Linien zeichnen
792             $self->maxmin($conf, $werte);
793            
794             # Gitter zeichnen
795             $self->wire($conf, $data);
796            
797             # Axis (Titel ... usw
798             $self->axis($conf, $werte);
799            
800             if( $conf->{count} > 0 && ( $conf->{typ} eq 'HASH' || $self->cget(-look)))
801             {
802             my $z = 0;
803             my $data = ($self->cget(-look) ? $self->{look} : $werte);
804             my $w;
805             my $td = $self->cget(-threed) || 0;
806             my $th = round($td / 3) if(defined $td);
807             my $ti = -1;
808            
809             foreach my $name (sort { $self->sorter } keys %{$data}) {
810             $ti++;
811             my @linepoints;
812             my $i = 0;
813             my ($xi, $yi, $xi_old, $yi_old);
814            
815             foreach my $point (@{$data->{$name}}) {
816             $xi = $conf->{x_null} + ((round( ($conf->{width} - $conf->{x_null})/$conf->{count})) * $i++);
817             push(@linepoints, $xi);
818             $yi = $conf->{y_null} - (( $conf->{y_null} - $conf->{ypad_top})/$conf->{max_value} * $point);
819             push(@linepoints, $yi);
820            
821            
822             # 3d
823             if( $td && $#linepoints > 1 ) {
824             my $winkel = winkel( ($xi - $xi_old), ($yi - $yi_old) );
825            
826             # Top
827             my $top = $self->createPolygon(
828             $xi, $yi,
829             ($xi + $td), ($yi - $td),
830             ($xi_old + $td), ($yi_old - $td),
831             ($xi_old), ($yi_old),
832            
833             -fill => $self->color_change(
834             $self->color($name, $point),
835             ( $yi_old >= $yi ? $conf->{light_top} : $conf->{light_side} ),
836             ),
837             -outline => $self->color($name, $point),
838             );
839            
840             # Bottom
841             my $bottom = $self->createPolygon(
842             $xi, ($yi + $th),
843             ($xi + $td), ($yi - $td + $th),
844             ($xi_old + $td), ($yi_old - $td + $th),
845             ($xi_old), ($yi_old + $th),
846            
847             -fill => $self->color_change( $self->color($name, $point), $conf->{light_side} ),
848             -outline => $self->color($name, $point),
849             ) if($winkel > 45 && $yi < $yi_old);
850            
851            
852             # Side
853             my $side = $self->createPolygon(
854             $xi, $yi,
855             ($xi ), ($yi + $th),
856             $xi_old, ($yi_old + $th),
857             $xi_old , $yi_old,
858            
859             -fill => $self->color_change( $self->color($name, $point), $conf->{light_front}),
860             -outline => $self->color($name, $point),
861             );
862             }
863            
864            
865             # Values
866             $self->createText($xi+12, $yi-12,
867             -text => sprintf($self->cget(-printvalue), '', $werte->{$point}),
868             -anchor => 'n',
869             -font => $conf->{font},
870             -fill => $self->cget(-titlecolor)
871             ) if($self->cget(-printvalue));
872            
873             # Dots
874             $self->createRectangle($xi-$self->cget(-dots), $yi-$self->cget(-dots),
875             $xi+$self->cget(-dots), $yi+$self->cget(-dots),
876             -fill => 'gray65',
877             -width => 1,
878             ) if($self->cget(-dots));
879            
880             # 3d (Abschluss)
881             if( $td && $i >= ( $#{$data->{$name}} + 1 ) ) {
882             # Side
883             $self->createPolygon(
884             $xi, $yi,
885             ($xi + $td), ($yi - $td),
886             ($xi + $td), ($yi - $td + $th),
887             $xi , $yi + $th,
888            
889             -fill => $self->color_change( $self->color($name, $point), $conf->{light_side} ),
890             -outline => $self->color($name, $point),
891             );
892             }
893            
894             # Graph Line
895             $self->{elements}->{$name} = $self->createLine(
896             $xi, $yi,
897             $xi_old, $yi_old,
898             -width => $self->cget(-linewidth),
899             -fill => $self->color($name, $point),
900             ) if($xi_old);
901            
902            
903             $xi_old = $xi;
904             $yi_old = $yi;
905             }
906            
907             }
908            
909             # balloon
910             $self->balloon($self->{elements}, $werte);
911            
912             # Legend
913             $self->legend($data, $conf);
914             }
915             }
916            
917             #-------------------------------------------------
918             sub color {
919             #-------------------------------------------------
920             my $self = shift || return error("No Objekt!");
921             return undef unless(ref $self eq __PACKAGE__);
922             my $name = shift || return error("No Name in color");
923             my $wert = shift;
924             my $color = $self->{colors}->{$name} || 'black';
925            
926             if(defined $self->{ranges}->{$name} && defined $wert) {
927             foreach $color ( keys %{$self->{ranges}->{$name}} ) {
928             my ($min, $max) = @{ $self->{ranges}->{$name}->{$color} };
929             if($wert >= $min and $wert <= $max) {
930             $self->debug('Name: %s, Color: %s, Wert: %g', $name, $color, ($wert || 'undef'));
931             return $color;
932             };
933             }
934             }
935            
936            
937             return $color;
938             }
939            
940            
941             #-------------------------------------------------
942             sub redraw {
943             #-------------------------------------------------
944             my $self = shift || return error("No Objekt!");
945             return undef unless(ref $self eq __PACKAGE__);
946             $self->debug('Redraw');
947             $self->set();
948             }
949            
950            
951             #-------------------------------------------------
952             sub automatic {
953             #-------------------------------------------------
954             my $self = shift || return error("No Objekt!");
955             return undef unless(ref $self eq __PACKAGE__);
956             return uc($self->cget(-type)) if($self->cget(-type));
957            
958             my $data = shift || $self->{data};
959            
960             my $type;
961            
962             if(ref $data eq 'ARRAY') {
963             $type = 'LINE'
964             } elsif (ref $data eq 'HASH') {
965             foreach my $n (keys %$data) {
966             if(ref $data->{$n} eq 'ARRAY') {
967             $type = 'LINE';
968             last;
969             } elsif (ref $data->{$n} eq 'HASH'){
970             $type = 'BARS';
971             last;
972             } else {
973             $type = 'CIRCLE';
974             last;
975             }
976             }
977             }
978             return $type;
979             }
980            
981             #-------------------------------------------------
982             sub set {
983             #-------------------------------------------------
984             my $self = shift || return error("No Objekt!");
985             return undef unless(ref $self eq __PACKAGE__);
986             my $data = shift;
987            
988             return error('The Widget has no width and height values, you must pack before you can set!')
989             unless($self->width || $self->height);
990            
991             # Make a LineGraph
992             if(ref $data eq 'ARRAY') {
993             my $werte;
994             $werte->{' '} = $data;
995             $data = $werte;
996             }
997            
998             my $type = $self->automatic( $data );
999            
1000             $self->{data} = $data if($data);
1001            
1002             if( $type eq 'LINE' ) {
1003             $self->draw_line($data);
1004            
1005             } elsif( $type eq 'CIRCLE' ) {
1006             $self->draw_circle($data);
1007            
1008             } elsif( $type eq 'BARS' ) {
1009             $self->draw_bars($data);
1010            
1011             } elsif( $type eq 'HBARS' ) {
1012             $self->draw_horizontal_bars( $data );
1013            
1014             } else {
1015             return error("Option \'-type\' is incorrect! ($type)");
1016             }
1017            
1018            
1019             }
1020            
1021             #-------------------------------------------------
1022             sub window_size {
1023             #-------------------------------------------------
1024             my $self = shift || return error("No Objekt!");
1025             return undef unless(ref $self eq __PACKAGE__);
1026            
1027             my ($width, $height);
1028             my $conf = $self->{conf};
1029             $self->update;
1030             return unless( $self->cget(-fill) );
1031             unless(defined $conf->{width} && $conf->{width} > 1 && defined $conf->{height} && $conf->{height} > 1) {
1032             $width = $self->width;
1033             $height = $self->height;
1034             } else {
1035             $width = ( $self->cget(-fill) eq 'x' || $self->cget(-fill) eq 'both' ? $self->width : $conf->{width} );
1036             $height = ( $self->cget(-fill) eq 'y' || $self->cget(-fill) eq 'both' ? $self->height : $conf->{height} );
1037             }
1038             $self->debug('Width: %d, Height: %d', $width, $height);
1039             return ($width, $height);
1040             }
1041            
1042             #-------------------------------------------------
1043             sub reference {
1044             #-------------------------------------------------
1045             my $self = shift || return error("No Objekt!");
1046             return undef unless(ref $self eq __PACKAGE__);
1047             my $data = shift || $self->{data} || return;
1048             my $reference = $self->cget(-reference) || return $data;
1049             my ($ref_name, $ref_value) = split(/,/, $reference);
1050            
1051             if(ref $data eq 'HASH') {
1052             my %werte = %$data;
1053             my $summe;
1054             foreach (keys %werte) {
1055             $summe+=$werte{$_};
1056             }
1057             $werte{$ref_name} = $ref_value - $summe;
1058             return \%werte;
1059             }
1060             }
1061            
1062             #-------------------------------------------------
1063             sub clear {
1064             #-------------------------------------------------
1065             my $self = shift || return error("No Objekt!");
1066             return undef unless(ref $self eq __PACKAGE__);
1067             $self->{data} = undef;
1068             $self->{look} = undef;
1069             $self->{colors} = undef;
1070             $self->redraw;
1071             }
1072            
1073             #-------------------------------------------------
1074             sub variable {
1075             #-------------------------------------------------
1076             my ($graph, $vref) = @_;
1077            
1078             $graph->{watch}->Unwatch
1079             if(defined $graph->{watch}); # Stoppen, falls ein Watch exisitiert
1080            
1081             my $store = [sub {
1082             my($self, $key, $new_val) = @_;
1083             $self->Store($key, $new_val); # Stopft den neuen Wert ins Watch
1084             my $args = $self->Args(-store); # Nimmt warn Argumente
1085             $args->[0]->set($args->[1]); # Ruft warn interne Routine auf
1086             }, $graph, $vref];
1087            
1088             $graph->{watch} = Tie::Watch->new(
1089             -variable => $vref,
1090             -store => $store );
1091            
1092             $graph->set($vref);
1093            
1094             $graph->OnDestroy( [sub {$_[0]->{watch}->Unwatch}, $graph] );
1095             } # end variable
1096            
1097             #-------------------------------------------------
1098             sub ReadConfig {
1099             #-------------------------------------------------
1100             # Liest warn Daten und oder berechnet den Confighash
1101             my $self = shift || return error("No Objekt!");
1102             return undef unless(ref $self eq __PACKAGE__);
1103             my $data = shift || return error("No Data!");
1104             my $conf;
1105            
1106             # Config
1107             $self->config($data);
1108            
1109             # Typ der Daten
1110             $conf->{typ} = ref $data;
1111            
1112             # Display Typ
1113             $conf->{type} = uc($self->cget(-type));
1114            
1115             # Font
1116             $conf->{font} = $self->cget(-font);
1117            
1118             # Standartcolor
1119             $conf->{fg} = $self->cget(-foreground);
1120            
1121             # Headroom
1122             $conf->{headroom} = ($self->cget(-headroom) / 100) + 1;
1123            
1124             # Light in 3D
1125             my $light = $self->cget(-light);
1126             $conf->{light_top} = $light->[0];
1127             $conf->{light_side} = $light->[1];
1128             $conf->{light_front} = $light->[2];
1129            
1130             # Windowsize
1131             ($conf->{width}, $conf->{height}) = $self->window_size();
1132             return unless($conf->{width} or $conf->{height});
1133            
1134             $self->{conf}->{width} = $conf->{width};
1135             $self->{conf}->{height} = $conf->{height};
1136            
1137             # Padding
1138             my $padding = $self->cget(-padding);
1139             $conf->{xpad} = $padding->[3];
1140             $conf->{xpad_right} = $padding->[1];
1141             $conf->{ypad} = $padding->[2];
1142             $conf->{ypad_top} = $padding->[0];
1143            
1144             $conf->{width} -= $conf->{xpad_right};
1145             $conf->{height} -= $conf->{ypad_top};
1146            
1147             # Title
1148             $conf->{title} = $self->cget(-title);
1149             $conf->{titlecolor} = $self->cget(-titlecolor);
1150            
1151             # Coordinates
1152             $conf->{y_null} = $conf->{height} - $conf->{ypad}; # 0 Koordinate y-Achse
1153             $conf->{x_null} = $conf->{xpad}; # 0 Koordinate x-Achse
1154            
1155             # Werte zaehlen
1156             $conf->{count} = 0;
1157             if($conf->{typ} eq 'ARRAY') {
1158             $conf->{count} = $#$data + 1;
1159             } elsif($conf->{typ} eq 'HASH' && $self->cget(-look) && $conf->{type} eq 'LINE') {
1160             $conf->{count} = $self->cget(-look);
1161             } elsif($conf->{typ} eq 'HASH' && $conf->{type} eq 'LINE') {
1162             # Durchzaehlen der Werte
1163             foreach ( keys %$data ) {
1164             $conf->{count} = $#{$data->{$_}}
1165             if(ref $data->{$_} eq 'ARRAY' && $#{$data->{$_}} > $conf->{count});
1166             }
1167            
1168             } else {
1169             foreach ( keys %$data ) {
1170             next if(ref $data->{$_});
1171             $conf->{count}++;
1172             }
1173             }
1174            
1175             $self->{cfg} = $conf;
1176             return $conf;
1177             }
1178            
1179             #-------------------------------------------------
1180             sub axis {
1181             #-------------------------------------------------
1182             my $self = shift || return error("No Objekt!");
1183             return undef unless(ref $self eq __PACKAGE__);
1184             my $conf = shift || return error("No Config");
1185             my $werte = shift || return error("No Data");
1186            
1187            
1188             goto NOAXIS
1189             if($conf->{type} eq 'CIRCLE');
1190            
1191             # Labels
1192             $self->labels();
1193            
1194             # Threed
1195             my $td = $self->cget(-threed) || 0;
1196            
1197            
1198             # X - K O O R D I N A T E ------------------------------
1199             $self->createLine(
1200             $conf->{x_null}, $conf->{y_null},
1201             $conf->{width}, $conf->{y_null},
1202             -width => 1,
1203             -fill => $conf->{fg},
1204             );
1205            
1206            
1207             # X-Ticks
1208             if($conf->{type} eq 'HBARS' || $conf->{type} eq 'LINE') {
1209             for(my $i = 0; $i <= $self->cget(-xtick); $i++) {
1210             my $x = $conf->{x_null} + (round( ($conf->{width} - $conf->{x_null})/$self->cget(-xtick)) * $i);
1211            
1212             $self->createLine(
1213             $x, ( $conf->{height} - ($conf->{ypad} + 5) ),
1214             $x, $conf->{y_null},
1215             -width => 1,
1216             -fill => $conf->{fg},
1217             );
1218             $self->createText(
1219             $x, $conf->{y_null},
1220             -text => sprintf(' '.$self->cget(-xformat), ( ($conf->{type} eq 'HBARS' ? $conf->{max_value} : $conf->{count}) / $self->cget(-xtick)) * $i),
1221             -anchor => 'n',
1222             -font => $conf->{font},
1223             -fill => $conf->{fg},
1224             ) if($i);
1225             }
1226             } else {
1227             my $i = -1;
1228             foreach my $name ( sort { $self->sorter } keys %$werte) {
1229             next if(ref $werte->{$name});
1230             $i++;
1231             my $text = sprintf($self->cget(-xformat), $name);
1232             my $x = $self->calc_x($i+1);
1233            
1234             $self->createLine(
1235             $x, ($conf->{height}-($conf->{ypad}+5)),
1236             $x, $conf->{y_null},
1237             -width => 1,
1238             -fill => $conf->{fg},
1239             );
1240             $self->createText($x, $conf->{y_null},
1241             -text => $text,
1242             -anchor => 'n',
1243             -font => $conf->{font},
1244             -fill => $conf->{fg},
1245             );
1246             }
1247             }
1248             # X - K O O R D I N A T E ---------BOTTOM----------------
1249            
1250            
1251             # Y - K O O R D I N A T E -------------------------------
1252             $self->createLine(
1253             $conf->{x_null}, $conf->{y_null},
1254             $conf->{x_null}, $conf->{ypad_top},
1255             -width => 1,
1256             -fill => $conf->{fg},
1257             );
1258            
1259             $self->createLine(
1260             $conf->{x_null}+$td, $conf->{y_null}-$td,
1261             $conf->{x_null}+$td, $conf->{ypad_top}-$td,
1262             -width => 1,
1263             -fill => $self->cget(-wire),
1264             ) if($td);
1265            
1266            
1267             if($conf->{type} eq 'HBARS') {
1268             my $i = 0.5;
1269             foreach my $name ( sort { $self->sorter } keys %$werte) {
1270            
1271             my $y = ($conf->{y_null}) - (int(($conf->{y_null} - $conf->{ypad_top}) / $conf->{count} + 0.99) * $i++);
1272            
1273             $self->createLine(
1274             $conf->{x_null}, $y,
1275             $conf->{x_null}-5, $y,
1276             -width => 1,
1277             -fill => $conf->{fg},
1278             );
1279            
1280             $self->createText($conf->{x_null}-8, $y,
1281             -text => $name,
1282             -anchor => 'e',
1283             -font => $conf->{font},
1284             -fill => $conf->{fg},
1285             );
1286             }
1287             } else {
1288             for (my $i = 0; $i <= $self->cget(-ytick); $i++) {
1289             next unless($i);
1290            
1291             my $y = ($conf->{y_null}) - (round( ( $conf->{y_null} - $conf->{ypad_top} )/$self->cget(-ytick)) * $i);
1292             $self->createLine(
1293             $conf->{x_null}, $y,
1294             $conf->{x_null}-5, $y,
1295             -width => 1,
1296             -fill => $conf->{fg},
1297             );
1298            
1299             $self->createText($conf->{x_null}-8, $y,
1300             -text => sprintf($self->cget(-yformat), (($conf->{max_value}/$self->cget(-ytick)) * $i)), -anchor => 'e',
1301             -font => $conf->{font},
1302             -fill => $conf->{fg},
1303             );
1304             }
1305             }
1306             # Y - K O O R D I N A T E ---------BOTTOM----------------
1307            
1308             NOAXIS:
1309            
1310             # Titel
1311             $self->createText(
1312             ($conf->{width} / 2), $self->cget(-lineheight),
1313             -text => $conf->{title},
1314             -justify => 'center',
1315             -fill => $conf->{titlecolor},
1316             ) if($conf->{title});
1317             }
1318            
1319             #-------------------------------------------------
1320             sub maxmin {
1321             #-------------------------------------------------
1322             my $self = shift || return error("No Objekt!");
1323             return undef unless(ref $self eq __PACKAGE__);
1324             my $conf = shift || return error("No Config");
1325             my $werte = shift || return error("No Data");
1326             my $MAX;
1327            
1328             if($conf->{typ} eq 'HASH' && $conf->{type} eq 'LINE')
1329             {
1330             $MAX->{$conf->{title}}->{min} = 10000 unless $MAX->{$conf->{title}}->{min};
1331             $MAX->{$conf->{title}}->{max} = 0 unless $MAX->{$conf->{title}}->{max};
1332             my $data = ($self->cget(-look) ? $self->{look} : $werte);
1333             foreach my $name (keys %{$data}) {
1334             foreach my $value (@{$data->{$name}}) {
1335             $MAX->{$conf->{title}}->{max} = $value if( $MAX->{$conf->{title}}->{max} <= $value );
1336             $MAX->{$conf->{title}}->{min} = $value if( $MAX->{$conf->{title}}->{min} >= $value );
1337             $MAX->{$conf->{title}}->{avg} =
1338             ( $MAX->{$conf->{title}}->{max} - $MAX->{$conf->{title}}->{min} ) / 2 +
1339             $MAX->{$conf->{title}}->{min};
1340             }
1341             }
1342             $conf->{max_value} = $self->cget(-max)
1343             ? $self->cget(-max)
1344             : $MAX->{$conf->{title}}->{max} * $conf->{headroom};
1345             }
1346             elsif($conf->{typ} eq 'ARRAY')
1347             {
1348             $MAX->{$conf->{title}}->{min} = 10000 unless $MAX->{$conf->{title}}->{min};
1349             $MAX->{$conf->{title}}->{max} = 0 unless $MAX->{$conf->{title}}->{max};
1350             foreach my $value (@{$werte}) {
1351             $MAX->{$conf->{title}}->{max} = $value if( $MAX->{$conf->{title}}->{max} <= $value );
1352             $MAX->{$conf->{title}}->{min} = $value if( $MAX->{$conf->{title}}->{min} >= $value );
1353             $MAX->{$conf->{title}}->{avg} =
1354             ( $MAX->{$conf->{title}}->{max} - $MAX->{$conf->{title}}->{min} ) / 2 +
1355             $MAX->{$conf->{title}}->{min};
1356             }
1357             $conf->{max_value} = $self->cget(-max)
1358             ? $self->cget(-max)
1359             : $MAX->{$conf->{title}}->{max} * $conf->{headroom};
1360             }
1361             elsif ($conf->{typ} eq 'HASH')
1362             {
1363             $MAX->{$conf->{title}}->{min} = 10000 unless $MAX->{$conf->{title}}->{min};
1364             $MAX->{$conf->{title}}->{max} = 0 unless $MAX->{$conf->{title}}->{max};
1365            
1366             foreach my $name (keys %{$werte}) {
1367             next if ref $werte->{$name};
1368             my $value = $werte->{$name} || 0;
1369             $MAX->{$conf->{title}}->{max} = $value if( $MAX->{$conf->{title}}->{max} <= $value );
1370             $MAX->{$conf->{title}}->{min} = $value if( $MAX->{$conf->{title}}->{min} >= $value ); $MAX->{$conf->{title}}->{avg} =
1371             $MAX->{$conf->{title}}->{avg} =
1372             ( $MAX->{$conf->{title}}->{max} - $MAX->{$conf->{title}}->{min} ) / 2 +
1373             $MAX->{$conf->{title}}->{min};
1374             }
1375             $conf->{max_value} = $self->cget(-max)
1376             ? $self->cget(-max)
1377             : $MAX->{$conf->{title}}->{max} * $conf->{headroom};
1378             }
1379            
1380             $conf->{max_value} = 1 unless($conf->{max_value});
1381            
1382             # MAX-MIN Linien
1383             if($self->cget(-maxmin) && $conf->{max_value} && ! $conf->{type} eq 'CIRCLE') {
1384             my $xa = $conf->{x_null};
1385             my $xe = $conf->{width}+10;
1386             my $y = $conf->{y_null} - int((($conf->{y_null})/$conf->{max_value}) * $MAX->{$conf->{title}}->{min});
1387            
1388             if($conf->{type} !~ /BARS/) {
1389             $self->createLine($xa, $y, $xe, $y,
1390             -width => 1,
1391             -fill => 'gray65'); # MIN-Linie
1392            
1393             $self->createText($xe-20, $y,
1394             -text => sprintf($self->cget(-printvalue) || '%g', $MAX->{$conf->{title}}->{min}),
1395             -anchor => 'se',
1396             -font => $conf->{font},
1397             -fill => 'gray65');
1398            
1399            
1400             $y = $conf->{y_null} - int((($conf->{y_null})/$conf->{max_value}) * $MAX->{$conf->{title}}->{avg});
1401             $self->createLine($xa, $y, $xe, $y,
1402             -width => 1,
1403             -fill => 'gray65'); # AVG-Linie
1404            
1405             $self->createText($xe-20, $y,
1406             -text => sprintf($self->cget(-printvalue) || '%g', $MAX->{$conf->{title}}->{avg}),
1407             -anchor => 'se',
1408             -font => $conf->{font},
1409             -fill => 'gray65');
1410            
1411            
1412            
1413             $y = $conf->{y_null} - int((($conf->{y_null})/$conf->{max_value}) * $MAX->{$conf->{title}}->{max}),
1414             $self->createLine($xa, $y, $xe, $y,
1415             -width => 1,
1416             -fill => 'gray65'); # AVG-Linie
1417            
1418             $self->createText($xe-20, $y,
1419             -text => sprintf($self->cget(-printvalue) || '%g', $MAX->{$conf->{title}}->{max}),
1420             -anchor => 'se',
1421             -font => $conf->{font},
1422             -fill => 'gray65');
1423             }
1424             }
1425             # --
1426            
1427             }
1428            
1429            
1430             #-------------------------------------------------
1431             sub draw_circle {
1432             #-------------------------------------------------
1433             # Plot LineStats
1434             my $self = shift || return error("No Objekt!");
1435             return undef unless(ref $self eq __PACKAGE__);
1436             my $data = shift || return;
1437            
1438             # Check
1439             return warn("Your data is incorrect, i need a Hashreference!")
1440             unless(ref $data eq 'HASH');
1441            
1442            
1443             my $werte = $self->reference($data);
1444             my $conf = $self->ReadConfig($werte) || return;
1445            
1446             $self->delete('all');
1447            
1448             # MaxMin Werte ermitteln und ggf Linien zeichnen
1449             $self->maxmin($conf, $werte);
1450            
1451             # Axis (Titel ... usw
1452             $self->axis($conf, $werte);
1453            
1454             # Sizes
1455             my $width = ($self->cget(-legend) ? $conf->{height} : $conf->{width});
1456             my $height = $conf->{y_null};
1457            
1458             # Shadow
1459             $self->createOval(
1460             ($conf->{x_null} + $self->cget(-shadowdepth) ), ($conf->{ypad_top} + $self->cget(-shadowdepth)),
1461             ($width + $self->cget(-shadowdepth)), ($height + $self->cget(-shadowdepth)),
1462             -fill => $self->cget(-shadow),
1463             -outline => $self->cget(-shadow),
1464             -width => 0,
1465             ) if($self->cget(-shadowdepth)); # Schatten
1466            
1467             # Segments
1468             my ($summe, $start, $count, $grad, $x, $y);
1469             foreach ( keys %$werte ) { $summe+=$werte->{$_} };
1470             $start = 0;
1471             $count = 0;
1472            
1473             foreach my $name (sort { $self->sorter } keys %$werte ) {
1474             my $col = $self->{colors}->{$name};
1475             next unless $werte->{$name};
1476             $grad = (360/$summe) * $werte->{$name};
1477             $grad = 359.99 if($grad == 360);
1478            
1479             $self->{elements}->{$name} = $self->createArc(
1480             $conf->{x_null}, $conf->{ypad_top},
1481             $width, $height,
1482             -width => $self->cget(-linewidth),
1483             -fill => $col,
1484             -start => $start,
1485             -extent => $grad,
1486             );
1487            
1488             $start+=$grad;
1489             }
1490            
1491             # balloon
1492             $self->balloon($self->{elements}, $werte);
1493            
1494             # Legend
1495             $self->legend($werte);
1496             }
1497            
1498             #-------------------------------------------------
1499             sub labels {
1500             #-------------------------------------------------
1501             my $self = shift || return error("No Objekt!");
1502             return undef unless(ref $self eq __PACKAGE__);
1503             my $conf = $self->{cfg};
1504            
1505             # X-Achse --------------------------------
1506             if($self->cget(-xlabel)) {
1507             $self->createLine(
1508             $conf->{width} - ($conf->{width} / 10), $conf->{y_null} - 10,
1509             $conf->{width} - 5, $conf->{y_null} - 10,
1510             -arrow => 'last',
1511             -fill => $conf->{fg},
1512             );
1513            
1514             $self->createText(
1515             $conf->{width} - ($conf->{width} / 10) - 5, $conf->{y_null} - 10,
1516             -text => $self->cget(-xlabel),
1517             -font => $conf->{font},
1518             -fill => $conf->{fg},
1519             -anchor => 'e',
1520             );
1521             }
1522             # ---------------------------------------
1523            
1524             # Y-Achse --------------------------------
1525             if($self->cget(-ylabel)) {
1526             $self->createLine(
1527             $conf->{x_null} + 10, $conf->{ypad_top} - 5,
1528             $conf->{x_null} + 10, $conf->{ypad_top} + ($conf->{height} / 10),
1529             -arrow => 'first',
1530             -fill => $conf->{fg},
1531             );
1532            
1533             $self->createText(
1534             $conf->{x_null} + 15, $conf->{ypad_top} + ($conf->{height} / 10),
1535             -text => $self->cget(-ylabel),
1536             -font => $conf->{font},
1537             -anchor => 'w',
1538             -fill => $conf->{fg},
1539             );
1540             }
1541             # ---------------------------------------
1542            
1543             }
1544            
1545            
1546             #-------------------------------------------------
1547             sub legend {
1548             #-------------------------------------------------
1549             my $self = shift || return error("No Objekt!");
1550             return undef unless(ref $self eq __PACKAGE__);
1551             my $data = shift || return error("No Data!");
1552             my $conf = $self->{cfg};
1553             return unless($self->cget(-legend));
1554            
1555             my $c = 0;
1556             my $fw = $self->cget(-lineheight) || 15;
1557            
1558             foreach my $name (sort { $self->sorter } keys %$data) {
1559             my $x = $conf->{width};
1560             my $y = $fw + ( $fw * $c ); # XXX
1561            
1562             my $thick = $self->cget(-dots) || 5;
1563            
1564             $self->createRectangle($x, $y,
1565             $x-$thick, $y-$thick,
1566             -fill => $self->{colors}->{$name},
1567             -width => $self->cget(-linewidth),
1568             );
1569            
1570             $self->createText($x - ($thick*2), $y,
1571             -text => sprintf( $self->cget(-printvalue) || '%s: %s', $name, (ref $data->{$name} ? '' : $data->{$name}) ),
1572             -font => $conf->{font},
1573             -anchor => 'e',
1574             -fill => $conf->{fg},
1575             );
1576             $c++
1577             }
1578             }
1579            
1580            
1581             #-------------------------------------------------
1582             sub readData {
1583             #-------------------------------------------------
1584             my $self = shift || return error("No Objekt!");
1585             return undef unless(ref $self eq __PACKAGE__);
1586             my $c = $self->configure;
1587             my $config;
1588             foreach my $n ($c) {
1589             $config->{$n->[0]} = $n->[3];
1590             }
1591             }
1592            
1593            
1594             #-------------------------------------------------
1595             sub wire {
1596             #-------------------------------------------------
1597             my $self = shift || return error("No Objekt!");
1598             return undef unless(ref $self eq __PACKAGE__);
1599             my $conf = shift || warn "No Conf";
1600             my $data = shift;
1601            
1602             return unless( $self->cget(-wire) );
1603            
1604             # 3D
1605             my $td = $self->cget(-threed) || 0;
1606             # XXX More as one linegraphs in deep
1607             # $td *= scalar keys %$data;
1608            
1609             # Y-Achse
1610             my $ytick = ($conf->{type} eq 'HBARS' ? $conf->{count} : $self->cget(-ytick));
1611             $ytick = 1 unless $ytick;
1612            
1613             for (my $i = 0; $i <= $ytick; $i++) {
1614             my $y = ($conf->{y_null}) - (round( ( $conf->{y_null} - $conf->{ypad_top} )/$ytick) * $i);
1615             $self->createLine(
1616             $conf->{x_null}, $y,
1617             ($conf->{x_null} + $td), ($y - $td),
1618             ($conf->{width} + $td), ($y - $td),
1619             -width => 1,
1620             -fill => ($i >= $ytick ? $self->cget(-foreground) : $self->cget(-wire)),
1621             );
1622             }
1623            
1624             # X-Achse
1625             my $xtick = ( $conf->{typ} eq 'HASH' && $conf->{type} ne 'HBARS' && $conf->{type} ne 'LINE' ? $conf->{count} : $self->cget(-xtick) );
1626             $xtick = 1 unless $xtick;
1627            
1628             if($conf->{type} eq 'HBARS' || $conf->{type} eq 'LINE') {
1629             for(my $i = 0; $i <= $xtick; $i++) {
1630             my $x = $conf->{x_null} + (round( ($conf->{width} - $conf->{x_null})/$self->cget(-xtick)) * $i);
1631            
1632             $self->createLine(
1633             $x, $conf->{y_null}, ($x + $td),
1634             ($conf->{y_null} - $td), ($x + $td),
1635             ($conf->{ypad_top} - $td),
1636             -width => 1,
1637             -fill => ( $i >= $xtick ? $self->cget(-foreground) : $self->cget(-wire))
1638             );
1639             }
1640             } else {
1641             for(my $i = 0; $i <= $xtick; $i++) {
1642             my $x;
1643             if( $i < $xtick ) {
1644             $x = $self->calc_x($i+1);
1645             } else {
1646             $x = $conf->{width};
1647             }
1648             $self->createLine(
1649             $x, $conf->{y_null}, ($x + $td),
1650             ($conf->{y_null} - $td), ($x + $td),
1651             ($conf->{ypad_top} - $td),
1652             -width => 1,
1653             -fill => ( $i >= $xtick ? $self->cget(-foreground) : $self->cget(-wire))
1654             );
1655             }
1656             }
1657             }
1658            
1659             #-------------------------------------------------
1660             sub config {
1661             #-------------------------------------------------
1662             my $self = shift || return error("No Objekt!");
1663             return undef unless(ref $self eq __PACKAGE__);
1664             my $data = shift || return;
1665             my $cols = $self->cget(-colors);
1666             my @colors = split(/,/, $cols);
1667             my $config = $self->cget(-config);
1668             my $c = -1;
1669            
1670             foreach my $name( keys %$data) {
1671             next if(defined $self->{colors}->{$name} && ! defined $config->{$name});
1672             $c++;
1673             $c = -1 unless($colors[$c]);
1674            
1675             my $name_new = $config->{$name}->{'-title'}
1676             if(defined $config->{$name}->{'-title'});
1677            
1678             # Colors
1679             $self->{colors}->{($name_new || $name)} = $config->{$name}->{'-color'} || $colors[$c];
1680            
1681             # Ranges
1682             if($config->{$name}->{'-range'}) {
1683             $self->{ranges}->{($name_new || $name)} = $config->{$name}->{'-range'};
1684             }
1685            
1686             # Title
1687             if($config->{$name}->{'-title'}) {
1688             $data->{$config->{$name}->{'-title'}} = delete $data->{$name};
1689             $self->{data} = $data;
1690             }
1691            
1692             }
1693             }
1694            
1695             #-------------------------------------------------
1696             sub register {
1697             #-------------------------------------------------
1698             my $self = shift || return error("No Objekt!");
1699             return undef unless(ref $self eq __PACKAGE__);
1700             my $data = shift || return $self->{look};
1701            
1702             foreach my $name (keys %$data) {
1703             $self->{look}->{$name} = $data->{$name};
1704             }
1705             }
1706            
1707            
1708             #-------------------------------------------------
1709             sub look {
1710             #-------------------------------------------------
1711             my $self = shift || return error("No Objekt!");
1712             return undef unless(ref $self eq __PACKAGE__);
1713             my $data = shift || $self->{data} || return;
1714             return unless($self->cget(-look));
1715            
1716             foreach my $name (keys %$data) {
1717             push(@{$self->{look}->{$name}}, $data->{$name});
1718             splice(@{$self->{look}->{$name}}, 0, ($#{$self->{look}->{$name}} - $self->cget(-look)))
1719             if($#{$self->{look}->{$name}} >= $self->cget(-look));
1720             }
1721             }
1722            
1723             #-------------------------------------------------
1724             sub sorter {
1725             #-------------------------------------------------
1726             my $self = shift || return error("No Objekt!");
1727             return undef unless(ref $self eq __PACKAGE__);
1728             my $typ = shift || $self->cget(-sortnames);
1729            
1730             if($self->cget(-sortreverse)) {
1731             if($typ eq 'num') {
1732             $b <=> $a
1733             } else {
1734             $b cmp $a
1735             }
1736             } else {
1737             if($typ eq 'num') {
1738             $a <=> $b
1739             } else {
1740             $a cmp $b
1741             }
1742             }
1743            
1744             }
1745            
1746             #-------------------------------------------------
1747             sub balloon{
1748             #-------------------------------------------------
1749             my $self = shift || return error("No Objekt!");
1750             return; # XXX produce Memory Leaks
1751            
1752             return undef unless(ref $self eq __PACKAGE__);
1753             my $elements = shift || return;
1754             my $werte = shift || return error('No Values');
1755             my $bh;
1756            
1757             foreach my $name (keys %$werte) {
1758             my $wert = (ref $werte->{$name} eq 'ARRAY' ? $werte->{$name}->[$#{$werte->{$name}}] : $werte->{$name});
1759             $bh->{$elements->{$name}} =
1760             sprintf(
1761             $self->cget(-printvalue) || ($name && $wert ? '%s: %s' : '%s'), $name, $wert)
1762             if($wert);
1763             }
1764            
1765             $self->{balloon}->attach(
1766             $self,
1767             -balloonposition => 'mouse',
1768             -msg => $bh,
1769             ) if(defined $self->{balloon});
1770             }
1771            
1772             #-------------------------------------------------
1773             sub round {
1774             #-------------------------------------------------
1775             my $wert = shift || return 0;
1776             my $ret = sprintf('%d', $wert);
1777             # my $ret = int( $wert + 0.99);
1778             return $ret;
1779             }
1780            
1781             #-------------------------------------------------
1782             sub winkel {
1783             #-------------------------------------------------
1784             my $a = shift; # Width
1785             my $b = shift; # Heigth
1786            
1787             my $c = sqrt($a**2+$b**2);
1788            
1789             my $cos_phi = ($a**2+$c**2-$b**2) / (2*$a*$c);
1790            
1791             return rad2deg(acos $cos_phi);
1792             }
1793            
1794            
1795            
1796             #-------------------------------------------------
1797             sub error {
1798             #-------------------------------------------------
1799             my ($package, $filename, $line, $subroutine, $hasargs,
1800             $wantarray, $evaltext, $is_require, $hints, $bitmask) = caller(1);
1801             my $msg = shift || return undef;
1802             warn sprintf("ERROR in %s:%s #%d: %s",
1803             $package, $subroutine, $line, sprintf($msg, @_));
1804             return undef;
1805             }
1806            
1807             #-------------------------------------------------
1808             sub debug {
1809             #-------------------------------------------------
1810             my $self = shift || return error("No Objekt!");
1811             return undef unless(ref $self eq __PACKAGE__);
1812             my $msg = shift || return;
1813             return unless($self->cget(-debug));
1814             printf($msg, @_);
1815             print "\n";
1816             }
1817            
1818             #-------------------------------------------------
1819             sub val2name {
1820             #-------------------------------------------------
1821             my $hash = shift || return;
1822             my $val = shift || return;
1823            
1824             foreach my $name (keys %$hash) {
1825             if($hash->{$name} eq $val) {
1826             return $name
1827             }
1828             }
1829             }
1830            
1831             #-------------------------------------------------
1832             sub calc_x {
1833             #-------------------------------------------------
1834             my $self = shift || return error("No Objekt!");
1835             return undef unless(ref $self eq __PACKAGE__);
1836             my $fac = shift;
1837             my $conf = $self->{cfg};
1838            
1839             my $count = ($conf->{type} eq 'BARS' ? $conf->{count} : $self->cget(-xtick) ) + 1;
1840            
1841            
1842             my $erg = $conf->{x_null} +
1843             (round
1844             (
1845             ( $conf->{width} - $conf->{x_null} )
1846             / $count
1847             ) * $fac);
1848             $self->debug("CALC_X: Width: %d, Faktor = %d, Count: %d, Ergebniss = %d",
1849             $conf->{width}, $fac, $count, $erg);
1850            
1851             return $erg;
1852             }
1853            
1854             #-------------------------------------------------
1855             sub calc_y {
1856             #-------------------------------------------------
1857             my $self = shift || return error("No Objekt!");
1858             return undef unless(ref $self eq __PACKAGE__);
1859             my $fac = shift || 0;
1860             my $conf = $self->{cfg};
1861            
1862             my $erg = $conf->{y_null} -
1863             round(
1864             (
1865             $conf->{y_null} -
1866             $conf->{ypad_top}
1867             ) / $conf->{max_value}
1868             * $fac );
1869            
1870             return $erg;
1871             }
1872            
1873            
1874             1;
1875            
1876             =head1 EXAMPLES
1877            
1878             Please see for examples in 'demos' directory in this distribution.
1879            
1880             =head1 AUTHOR
1881            
1882             Frank Herrmann
1883             xpix@netzwert.ag
1884             http://www.netzwert.ag
1885            
1886             =head1 SEE ALSO
1887            
1888             Tk,
1889             Tk::Trace,
1890             Tk::Canvas,
1891            
1892             =cut
1893            
1894             __END__