File Coverage

blib/lib/TkUtil/Gui.pm
Criterion Covered Total %
statement 21 141 14.8
branch 0 80 0.0
condition 0 15 0.0
subroutine 7 20 35.0
pod 8 8 100.0
total 36 264 13.6


line stmt bran cond sub pod time code
1             package TkUtil::Gui;
2              
3 1     1   32247 use warnings;
  1         3  
  1         33  
4 1     1   7 use strict;
  1         6  
  1         36  
5 1     1   880 use Perl6::Attributes;
  1         54169  
  1         7  
6              
7             =head1 NAME
8              
9             TkUtil::Gui - Easy access to a Perl/Tk GUI state
10              
11             =head1 VERSION
12              
13             Version 0.01
14              
15             =cut
16              
17             our $VERSION = '0.01';
18              
19              
20             =head1 SYNOPSIS
21              
22             use TkUtil::Gui;
23              
24             my $gui = TkUtil::Gui->new(top => $mw);
25             $gui->Listbox(name => 'List')->pack;
26             $gui->Button(-text => 'Push Me')->pack;
27              
28             =head1 DESCRIPTION
29              
30             GUI's can be difficult to set up, but I have found that a lot of
31             code is required to merely get information out of a GUI to pass
32             to another application. This module attempts to rectify this.
33              
34             It does it's magic by acknowledging that all Tk widget creation
35             switches begin with a "-" and that most widgets have a I<-text>
36             or I<-textvariable> option (or both). In general, this module
37             assigns those for you. And remembers them.
38              
39             To create widgets, you do it like this:
40              
41             $frame = $mw->Frame->pack;
42             $gui = TkUtil::Gui->new(top => $frame);
43             $widget = $gui->Checkbutton(name => 'utm', -text => 'UTM')->pack;
44              
45             What magically happens is that $gui assigns a variable reference
46             for you to the Checkbutton, and there are methods to allow
47             you to set it or fetch it.
48              
49             If your GUI has an "OK" button (or something similar), you can (with
50             a single call) figure out the contents of the entire GUI, returned as
51             a hash.
52              
53             See eg/Gui.t for an example.
54              
55             More below.
56              
57             =head1 METHODS
58              
59             =cut
60              
61 1     1   9289 use strict;
  1         4  
  1         51  
62 1     1   8 use warnings;
  1         3  
  1         45  
63 1     1   6 use Perl6::Attributes;
  1         2  
  1         8  
64 1     1   5723 use Data::Dumper;
  1         9856  
  1         2086  
65              
66             =head2 B
67              
68             $gui = TkUtil::Gui->new(top => $top);
69              
70             I<$top> is the widget you want to create widgets in. You can change
71             the meaning of I<$top> at any time with the B method. But please,
72             only create one instance of TkUtil::Gui. You can create more, but
73             they don't know about each other, so dumping the state of a GUI won't
74             work like you expect.
75              
76             =cut
77              
78             sub new {
79 0     0 1   my $Class = shift;
80 0           my (%opts) = @_;
81 0           my $self = \%opts;
82 0           bless $self, $Class;
83 0           $.Class = $Class;
84 0           ./_require("top");
85 0           return $self;
86             }
87              
88             sub _require {
89 0     0     my ($self, $name) = @_;
90 0 0         die "$.Class - $name is required\n" unless defined $self->{$name};
91             }
92              
93             sub _specs {
94 0     0     my ($self, $widget) = @_;
95 0 0         return $widget->ConfigSpecs if defined $widget;
96             }
97              
98             sub _setvar {
99 0     0     my ($self, $widget, $default) = @_;
100 0           my %specs;
101             #print "_setvar got ", ref($widget), "\n";
102 0 0         %specs = ./_specs($widget) if (!ref($widget->ConfigSpecs));
103 0           my $var;
104 0 0         if (defined $specs{-variable}) {
105 0           my $vref = $widget->cget(-variable);
106 0 0         if (!defined $vref) {
107 0           $widget->configure(-variable => \$var);
108 0           $.vars{$widget} = \$var;
109 0           print "vref = $vref\n";
110             }
111             else {
112 0           $.vars{$widget} = $widget->cget(-variable);
113             }
114 0           ${$.vars{$widget}} = $default;
  0            
115             }
116 0           my $textvar;
117 0 0         if (defined $specs{-textvariable}) {
118 0           my $text;
119 0 0         $text = $widget->cget(-text) if defined $specs{-text};
120 0 0         if (!defined $widget->{-textvariable}) {
121 0           $widget->configure(-textvariable => \$textvar);
122 0           $.textvars{$widget} = \$textvar;
123             }
124             else {
125 0           $.textvars{$widget} = $widget->cget(-textvariable);
126             }
127 0 0         $textvar = $text if defined $text;
128 0 0         ${$.textvars{$widget}} = $default if ref($widget) eq 'Tk::Entry';
  0            
129             #print "text = $text\n";
130             }
131             }
132              
133             =head2 B
134              
135             This is method that intercepts any undefined entry points. You don't
136             I AUTOLOAD, it is Perl magic that intercepts any non-specified
137             function.
138              
139             Special options, all of which are optional.
140              
141             name [1]
142             vfrom [2]
143             packOpts [3]
144             onoff [4]
145             default [5]
146              
147             [1] this is the symbolic name by which this widget is referred. If this
148             is a widget you want to get information out of, then it should be named.
149             Buttons, which generally cause actions to happen, and don't have a lasting
150             state don't need this.
151              
152             [2] useful for Radiobuttons, where a variable reference is shared. This
153             points to the name of a Radiobutton to share with. See example in
154             eg/Gui.t
155              
156             [3] this is packing options, as an array reference
157              
158             [4] useful for Checkbuttons, the test to use to indicate the on and off
159             state. Specified like "on|off" or "true|false" or something similar.
160              
161             [5] a default value for this widget; only appropriate where a value
162             is meaningful. Currently, not Listboxes, though.
163              
164             =cut
165              
166             sub AUTOLOAD {
167 0     0     my ($self) = CORE::shift;
168 0           my @opts = @_;
169             #print "Opts = ", Dumper(\@opts);
170 0           my $name = our $AUTOLOAD;
171 0           $name =~ s/.*://;
172 0 0         return if $name eq 'DESTROY';
173 0           my %opts;
174             my %Opts;
175 0           my $leadarg;
176              
177             # special handling for Scrolled widget
178 0 0         $leadarg = shift(@opts) if $name eq 'Scrolled';
179 0 0         %opts = @opts unless @opts % 2;
180              
181             # Separate Tk from "Special" switches
182 0           my %Special;
183             my %Tk;
184 0 0         map {
185 0           $Special{$_} = $opts{$_} unless $_ =~ /^-/;
186 0 0         $Tk{$_} = $opts{$_} if $_ =~ /^-/;
187             } keys(%opts);
188              
189             # this is our magic variable
190 0           my $var;
191              
192             # handle special on/off switch for Checkboxes
193 0 0         if (defined $Special{onoff}) {
194 0           my ($on, $off) = split(/\|/, $Special{onoff});
195 0 0         $Tk{-onvalue} = $on unless defined $Tk{-onvalue};
196 0 0         $Tk{-offvalue} = $off unless defined $Tk{-offvalue};
197 0 0 0       $Special{default} = $off if defined $off && !defined $Special{default};
198             }
199             #print Dumper(\%Tk);
200 0           my @Tk = %Tk;
201 0 0         unshift(@Tk, $leadarg) if defined $leadarg;
202 0           my $w = $.top->$name(@Tk);
203             #print ref($w), Dumper(\%Special);
204              
205             # Ow = original widget
206 0           my $Ow = $w;
207 0           my $reffer = ref($w);
208 0 0 0       if ($reffer eq 'Tk::LabEntry' || $reffer eq 'Tk::BrowseEntry') {
209 0           $w = $w->Subwidget('entry');
210 0 0         $w = $w->Subwidget('entry') if ref($w) eq 'Tk::LabEntry';
211             }
212              
213 0           my $packOpts = $.packOpts;
214 0 0         $packOpts = $Special{packOpts} if defined $Special{packOpts};
215 0 0 0       if (defined $packOpts && ref($packOpts) eq 'ARRAY') {
216 0           $w->pack(@{$packOpts});
  0            
217             }
218              
219             #print ref($w), "\n";
220 0 0         if (defined $Special{vfrom}) {
221 0           my $fw = ./_widget_from_name($Special{vfrom});
222 0 0         $.vars{$w} = $.vars{$fw} if defined $fw;
223 0 0         ./_setvar($w, $Special{default}) unless defined $fw;
224             }
225             else {
226 0           ./_setvar($w, $Special{default});
227             #print "setting ", ref($w), " to $Special{default}\n";
228             }
229 0 0         $.name{$Special{name}} = $w if defined $Special{name};
230              
231             # return original widget
232 0           return $Ow;
233             }
234              
235             =head2 B
236              
237             $old = $gui->top($top);
238              
239             Changes parent widget for widget creation. Returns the current
240             definition of "top" before it was changed. This way, you can
241             safely change it in a function, then change it back before you
242             return.
243              
244             No argument means fetch and return the current value of I.
245              
246             =cut
247              
248             sub top {
249 0     0 1   my ($self, $top) = @_;
250 0 0         return $.top unless defined $top;
251 0           my $oldtop = $.top;
252 0 0         $.top = $top if defined $top;
253 0           return $oldtop;
254             }
255              
256             =head2 B
257              
258             @names = $gui->names();
259              
260             All of the names maintained by this instance of the class.
261              
262             =cut
263              
264             sub names {
265 0     0 1   my ($self) = @_;
266 0           return keys(%{$.name});
  0            
267             }
268              
269             =head2 B
270              
271             $vRef = $gui->vref($widget);
272              
273             Get the variable reference associated with this widget.
274              
275             =cut
276              
277             sub vref {
278 0     0 1   my ($self, $w) = @_;
279 0 0         return $.textvars{$w} if ref($w) eq 'Tk::Entry';
280 0           return $.vars{$w};
281             }
282              
283             =head2 B
284              
285             $gui->set($name, $value);
286              
287             Set the named widget to the specified value.
288              
289             =cut
290              
291             sub set {
292 0     0 1   my ($self, $name, $value) = @_;
293 0 0         return unless defined $name;
294 0           my $w = ./_widget_from_name($name);
295 0 0         if (!defined $w) {
296 0           print STDERR "$.Class - set called with unknown name ($name)\n";
297 0           return;
298             }
299 0           my $vref = ./vref($w);
300 0 0         $$vref = $value if defined $vref;
301 0 0 0       unless (ref($w) eq 'Tk::Radiobutton' || ref($w) eq 'Tk::Checkbutton') {
302 0           $vref = $.textvars{$w};
303 0 0         $$vref = $value if defined $vref;
304             }
305             }
306              
307             sub _widget_from_name {
308 0     0     my ($self, $name) = @_;
309 0           return $.name{$name};
310             }
311              
312             =head2 B
313              
314             $widget = $gui->widget($name);
315              
316             Access the widget equated with the specified name.
317              
318             =cut
319              
320             sub widget {
321 0     0 1   my ($self, $name) = @_;
322 0           return ./_widget_from_name($name);
323             }
324              
325             =head2 B
326              
327             $result = $gui->query_by_name($name, %opts);
328              
329             %opts can be:
330              
331             as_indices - if named widget being queried is Listbox, return
332             selected entries as indices vs. selected text
333              
334             $result will be an array reference for a Listbox. Even if only a single
335             entry is selected in your list.
336              
337             =cut
338              
339             sub query_by_name {
340 0     0 1   my ($self, $name, %opts) = @_;
341 0           my $w = $.name{$name};
342             #print ref($w), "\n";
343 0           my $as_indices = $opts{as_indices};
344 0 0         if (ref($w) eq 'Tk::Listbox') {
345 0           my @sel = $w->curselection;
346 0 0 0       return \@sel if defined $as_indices && $as_indices != 0;
347 0           map {
348 0           $_ = $w->get($_);
349             } @sel;
350 0           return \@sel;
351             }
352 0           my $vref;
353 0           $vref = $.vars{$w};
354 0 0         $vref = $.textvars{$w} unless defined $vref;
355 0 0         return defined $vref ? $$vref : undef;
356             }
357              
358             =head2 B
359              
360             %hash = $gui->as_hash(%opts);
361              
362             Fetch the entire contents of the GUI as a hash. This is your
363             specified name as the key, and the value is the dereferenced
364             variable reference maintained internally. For a Listbox, the
365             setting would be an array reference to the textual entries from
366             the Listbox (not indices) by default.
367              
368             %opts can be:
369              
370             as_indices - return Listbox contents as indices, not text
371              
372             =cut
373              
374             sub as_hash {
375 0     0 1   my ($self, %opts) = @_;
376 0           my @names = ./names();
377 0           my %result;
378 0           map {
379 0           my $result = ./query_by_name($_, %opts);
380 0           $result{$_} = $result;
381             } @names;
382 0           return %result;
383             }
384              
385             1;
386             =head1 AUTHOR
387              
388             X Cramps, C<< >>
389              
390             =head1 BUGS
391              
392             There are undoubtedly widgets I am not dealing with properly
393             here. Let me know what they are, and I'll see about adding code
394             to handle them properly (if possible).
395              
396             Please report any bugs or feature requests to
397             C, or through
398             the web interface at
399             L.
400             I will be notified, and then you'll
401             automatically be notified of progress on your bug as I make changes.
402              
403              
404              
405              
406             =head1 SUPPORT
407              
408             You can find documentation for this module with the perldoc command.
409              
410             perldoc TkUtil::Gui
411              
412              
413             You can also look for information at:
414              
415             =over 4
416              
417             =item * RT: CPAN's request tracker
418              
419             L
420              
421             =item * AnnoCPAN: Annotated CPAN documentation
422              
423             L
424              
425             =item * CPAN Ratings
426              
427             L
428              
429             =item * Search CPAN
430              
431             L
432              
433             =back
434              
435              
436             =head1 ACKNOWLEDGEMENTS
437              
438              
439             =head1 COPYRIGHT & LICENSE
440              
441             Copyright 2009 X Cramps, all rights reserved.
442              
443             This program is free software; you can redistribute it and/or modify it
444             under the same terms as Perl itself.
445              
446              
447             =cut
448              
449             1; # End of TkUtil::Gui