File Coverage

blib/lib/Tk/PerlInheritanceTree.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             Tk::PerlInheritanceTree - Display a graphical representation of the inheritance tree for a given class-name.
6              
7             =head1 SYNOPSIS
8              
9              
10             require Tk::PerlInheritanceTree;
11             ...
12             my $inheritance_tree = $main_window->PerlInheritanceTree()->pack;
13              
14             $inheritance_tree->classname('Tk::MainWindow');
15              
16              
17              
18             =head1 DESCRIPTION
19              
20             Tk::PerlInheritanceTree displays a graphical representation of the inheritance tree for a given class(package)-name. The nodes representing classnames have mouseclick bindings to open a Tk::PerlMethodList - widget. Tk::PerlInheritanceTree is a Tk::Frame-derived widget.
21              
22             PerlInheritanceTree.pm can be run as stand-alone application (see Examples section)
23              
24              
25              
26             =head1 SEE ALSO
27              
28             Documentation of Tk::PerlMethodList.
29              
30             =head1 METHODS
31              
32             B supports the following methods:
33              
34             =over 4
35              
36             =item B'A::Classname'B<)>
37              
38             Set the Classname-Entry to 'A::Classname' and show_classtree.
39              
40             =item B
41              
42             Display a tree for the given classname
43              
44             =back
45              
46              
47             =head1 OPTIONS
48              
49             B supports the following options:
50              
51             =over 4
52              
53             =item B<-classname>
54              
55             configure(-classname=>'A::Classname')
56             same as method classname()
57              
58             =item B<-gridsize>
59              
60             configure(-gridsize=>$size)
61             Set the distance between nodes to $size pixels. Defaults to 120.
62              
63             =item B<-multiple_methodlists>
64              
65             configure(-multiple_methodlists=>bool)
66             Allows multiple instances of PerlMethodList to be opened if set to a true value. Defaults to 0.
67              
68             =back
69              
70              
71             =head1 EXAMPLES
72              
73             Run PerlInheritanceTree from the console:
74              
75             perl -MTk::PerlInheritanceTree -e'Tk::PerlInheritanceTree::_test_'
76              
77             or:
78              
79             perl -MTk::PerlInheritanceTree -e'Tk::PerlInheritanceTree::_test_(shift)' Tk::Menu
80              
81              
82              
83             =head1 AUTHOR
84              
85             Christoph Lamprecht, ch.l.ngre@online.de
86              
87             =head1 COPYRIGHT AND LICENSE
88              
89             Copyright (C) 2006-2008 by Christoph Lamprecht
90              
91             This library is free software; you can redistribute it and/or modify
92             it under the same terms as Perl itself, either Perl version 5.8.7 or,
93             at your option, any later version of Perl 5 you may have available.
94              
95              
96              
97             =cut
98             package Tk::PerlInheritanceTree;
99             our $VERSION = 0.05;
100 1     1   3815 use warnings;
  1         4  
  1         42  
101 1     1   6 use strict;
  1         2  
  1         100  
102             require Class::Inspector;
103              
104             require Tk;
105             require Tk::NumEntry;
106             require Tk::GraphItems::TextBox;
107             require Tk::GraphItems::Connector;
108             require Tk::PerlMethodList;
109 1     1   6 use base 'Tk::Frame';
  1         2  
  1         1162  
110              
111             use Data::Dumper;
112              
113             Tk::Widget->Construct('PerlInheritanceTree');
114             unless (caller()){_test_()}
115              
116             sub Populate{
117             my ($self,$args)=@_;
118             $self->SUPER::Populate($args);
119             my $can = $self->Scrolled('Canvas',
120             -scrollregion=> [qw/0 0 200 200/]
121             )->pack(-expand =>1,
122             -fill =>'both'
123             );
124             my $c = $can->Subwidget('scrolled');
125             $self->{canvas}=$c;
126              
127             $self -> _setup_bindings;
128             my $bottom_f = $self->Frame->pack(-fill => 'x',
129             # -expand => 1,
130             );
131              
132             $self -> Label(-textvariable=>\$self->{status},
133             -relief =>'sunken'
134             )->pack(-fill => 'x',
135             # -expand => 1,
136             -padx => 10
137             );
138             my $bottom_left = $bottom_f->Frame->pack(-side => 'left',
139             -padx => 10,
140             );
141             my $bottom_right = $bottom_f->Frame->pack(-side => 'left',
142             -padx => 10,
143             );
144              
145             my $en = $bottom_left->Entry(-textvariable=>\$self->{class}
146             )->pack(-side =>'left',
147             );
148             my $bt = $bottom_left->Button(-text => 'Classtree',
149             -command => sub {$self->show_classtree()}
150             )->pack(-side =>'left',
151             -padx => 10,
152             );
153             $bottom_right->Label(-text => 'Gridsize:',
154             )->pack(-side => 'left',
155             -padx => 5,
156             );
157             $self->{gridsize} =$args->{'-gridsize'} ||= 120;
158             my $ne;
159             $ne = $bottom_right->NumEntry(-minvalue => 80,
160             -maxvalue => 200,
161             -increment => 20,
162             -width => 4,
163             -readonly => 1,
164             -textvariable => \$self->{gridsize},
165             -browsecmd => [$bt,'invoke'],
166             )->pack(-side => 'left',
167             );
168              
169             $en->bind('',sub{$bt->Invoke});
170             $self->ConfigSpecs(-background => [$c],
171             -classname => ['METHOD'],
172             -multiple_methodlists=> ['PASSIVE','','',0],
173             -gridsize => ['METHOD','','',$self->{gridsize}],
174             DEFAULT => [$c],
175             );
176              
177             $self;
178             }
179              
180             sub _setup_bindings{
181             my $self = shift;
182             my $c = $self->{canvas};
183              
184             ####create a Tk::GraphItems instance to set bindings###
185             my $dummy = Tk::GraphItems::TextBox->new(text=>'',
186             x =>0,
187             y =>0,
188             canvas=>$c);
189             $dummy->bind_class('<3>',sub{$self->node_clicked($_[0])});
190             $dummy->bind_class('',sub{$self->node_clicked($_[0])
191             unless $_[0]->was_dragged});
192             }
193              
194             sub _build_classtree{
195             my ($self,$row,$nr_nodes,$class,$succ,$succ_node) = @_;
196              
197             $succ ||= $self->{tree}||={};
198             $succ->{$class}={};
199             $self->{nodes}[$row]||=[];
200             my $col = (scalar@{$self->{nodes}[$row]}) +1;
201             my $node = Tk::GraphItems::TextBox->new(canvas => $self->{canvas},
202             text => $class,
203             y => 150,
204             x => 150,
205             );
206              
207             push @{$self->{nodes}[$row]} , $node;
208             if ($node && $succ_node){
209             Tk::GraphItems::Connector->new(source => $node,
210             target => $succ_node)
211             }
212             no strict 'refs';
213             my @parents = @{$class."::ISA"};
214             use strict;
215             $row++;
216             for my $parent(@parents){
217             $self->_build_classtree($row,scalar@parents,$parent,$succ->{$class},$node);
218             }
219             }
220             sub _place_nodes{
221             my $self = shift;
222             my $rows = @{$self->{nodes}};
223             my $gridsz = $self->cget('-gridsize');
224             my $bottom = ($rows-0.5)*$gridsz;
225             my $max_cols= 1 ;
226             for my $row(@{$self->{nodes}}){
227             $max_cols = @$row if @$row>$max_cols;
228             }
229             my $center = ($max_cols+1) /2 *$gridsz;
230             my $row = 0;
231             for my $nodes ( @{$self->{nodes}}){
232             my $cols = @$nodes;
233             my $col = 0;
234             for my $node(@$nodes){
235             $node->set_coords($center +(($col-($cols-1)/2)* $gridsz),
236             $bottom - $row * $gridsz);
237             $col++;
238             }
239             $row++;
240             }
241             $self->{canvas}->configure(-scrollregion=>[0,
242             0,
243             $center*2,
244             $bottom+ .5*$gridsz]);
245             }
246             sub classname{
247             my ($self,$class) = @_;
248             $self->{class} = $class;
249             $self->show_classtree;
250             }
251             sub gridsize{
252             my $self = shift;
253             $self->{gridsize} = $_[0] if ($_[0]);
254             return $self->{gridsize};
255             }
256             sub show_classtree{
257             my ($self) = @_;
258             my $class = $self->{class};
259             return unless ($class);
260             eval "require $class";
261              
262             unless (Class::Inspector->loaded($class)){
263             $self->{status} = "Error: Package '$class' not found !";
264             return;
265             }
266              
267             $self->{status} = "Showing inheritance tree for class '$class'";
268             $self->{tree} = {};
269             $self->{nodes}= [];
270             $self->_build_classtree(0,1,$class);
271             $self->_place_nodes;
272             $self->_place_nodes;
273             }
274             sub node_clicked{
275             my ($self,$node) = @_;
276             my $text = $node->text;
277             my $mml = $self->cget('-multiple_methodlists');
278             my $ml = $self->{m_list};
279             unless ($ml && $ml->Exists){
280             $ml = $self->PerlMethodList;
281             }
282            
283             $ml->configure(-classname=>$text,
284             -filter =>'');
285             $ml->show_methods;
286             $ml->deiconify;
287             $ml->focus;
288             if (!$mml){
289             $ml->protocol("WM_DELETE_WINDOW",sub{$ml->withdraw});
290             $self->{m_list} = $ml;
291             }else{
292             $ml->protocol("WM_DELETE_WINDOW",'');
293             }
294             }
295              
296             sub _test_{
297              
298             my $mw = Tk::tkinit();
299             my @cln;
300             @cln = (-classname => $_[0]) if $_[0];
301             my $cg =$mw->PerlInheritanceTree(@cln)
302             ->pack(-fill => 'both',
303             -expand => 1);
304             Tk::MainLoop();
305             }
306             1;
307             __END__