Brian Kelly > Term-Menus-2.83 > Term::Menus

Download:
Term-Menus-2.83.tar.gz

Dependencies

Annotate this POD

CPAN RT

Open  0
View/Report Bugs
Module Version: 2.83   Source  

NAME ^

Term::Menus - Create Powerful Terminal, Console and CMD Enviroment Menus

SYNOPSIS ^

use Term::Menus;

see METHODS section below

DESCRIPTION ^

Term::Menus allows you to create powerful Terminal, Console and CMD environment menus. Any perl script used in a Terminal, Console or CMD environment can now include a menu facility that includes sub-menus, forward and backward navigation, single or multiple selection capabilities, dynamic item creation and customized banners. All this power is simple to implement with a straight forward and very intuitive configuration hash structure that mirrors the actual menu architechture needed by the application. A separate configuration file is optional. Term::Menus is cross platform compatible.

Term::Menus was initially conceived and designed to work seemlessly with the perl based Network Process Automation Utility Module called Net::FullAuto (Available in CPAN :-) - however, it is not itself dependant on other Net::FullAuto components, and will work with *any* perl script/application.

Reasons to use this module are:

Usage questions should be directed to the Usenet newsgroup comp.lang.perl.modules.

Contact me, Brian Kelly <Brian.Kelly@fullautosoftware.net>, if you find any bugs or have suggestions for improvements.

What To Know Before Using

^

METHODS ^

pick - create a simple menu
    $pick = &pick ($list|\@list|['list',...],[$Banner]);

Where $list is a variable containing an array or list reference. This argument can also be a escaped array (sending a reference) or an anonymous array (which also sends a reference).

$Banner is an optional argument sending a customized Banner to top the simple menu - giving instructions, descriptions, etc. The default is "Please Pick an Item:"

Menu - create a complex Menu
    $pick  = &Menu ($list|\@list|['list',...],[$Banner]);

Where $pick is a variable containing an array or list reference of the pick or picks.

    @picks = &Menu ($Menu_1|\%Menu_1|{ Name => 'Menu_1' });

Where $Menu_1 is a hash reference to the top level Menu Configuration Hash Structure.

Menu Configuration Hash Structures

^

These are the building blocks of the overall Menu architecture. Each hash structure represents a menu screen. A single menu layer, has only one hash structure defining it. A menu with a single sub-menu will have two hash structures. The menus connect via the Result element of an Item - Item_1 - hash structure in parent menu %Menu_1:

   my %Menu_2=(

      Name   => 'Menu_2',
      Item_1 => {

         Text   => "]Previous[ is a ]Convey[ Utility",
         Convey => [ 'Good','Bad' ]
      },

      Select => 'One',
      Banner => "\n   Choose an Answer :"
   );

   my %Menu_1=(

      Name   => 'Menu_1',
      Item_1 => {

         Text   => "/bin/Utility - ]Convey[",
         Convey => [ `ls -1 /bin` ],
         Result => \%Menu_2,

      },

      Select => 'One',
      Banner => "\n   Choose a /bin Utility :"
   );

Menu Component Elements

^

Each Menu Configuration Hash Structure consists of elements that define and control it's behavior, appearance, constitution and purpose. An element's syntax is as you would expect it to be in perl - a key string pointing to an assocaited value: key => value. The following items list supported key names and ther associated value types:

Item Configuration Hash Structures

^

Each Menu Item can have an independant configurtion. Each Menu Configuration Hash Structure consists of elements that define and control it's behavior, appearance, constitution and purpose. An element's syntax is as you would expect it to be in perl - a key string pointing to an assocaited value: key => value. The following items list supported key names and ther associated value types:

Item Configuration Macros

^

Each Menu Item can utilize a very powerful set of configuration Macros. These constructs principally act as purveyors of information - from one menu to another, from one element to another. There are currently three available Macros:

ANONYMOUS SUBROUTINES AND MACROS ^

Term::Menus macros can be used directly in the body of anonymous subroutines! Ordinary subroutines can be used as illustrated above of course, but the macro values can only be passed as arguments to ordinary subroutines. This is much more complicated and less intuitive than using macros directly in the code itself. Below is an example of their usage. The author received a request a while back from a user, asking if it was possible to return the item number rather than it's text value. The answer of course is YES! The code below illustrates this:

   use Term::Menus;

   my @list=('One','Two','Three');

   my %Menu_1=(

      Item_1 => {

         Text    => "NUMBER - ]Convey[",
         Convey  => \@list,
         Result  => sub {
                           my $cnt=-1;my $selection=']Selected[';
                           foreach my $item (@list) {
                              $cnt++;
                              chomp($item);
                              last if -1<index $selection, $item;
                           } return "$cnt";
                        }
                        # Note use of ]Selected[ macro in
                        # anonymous subroutine body

      },

      Select => 'One',
      Banner => "\n   Choose a /bin Utility :"
   );

   my $selection=Menu(\%Menu_1);
   print "   \nSELECTION = $selection\n";

Anonymous subroutines can be assigned directly to "Item_1" (or Item_2, etc.) elements 'Convey' and 'Result' as well as to the Menu "Banner" element. Use of the these constructs over more traditional subroutines is encouraged because it means writing less code, while enabling the code that is written to be less complex, more intuitive and readable, and certainly easier to maintain. The same anonymous routine can be use in multipe Menus or Items of a single Menu by assigning that routine to a variable, and then assigning the variable instead.

NOTE: To force a return to a parent menu (assuming there is one) from a subroutine assigned to a Result element, just return '<' from the subroutine. This is extremely useful when there is a desire to process a selection, and then return to the parent menu when processing is complete. To return to any ancestor Menu in the stack, return this macro from the subroutine: {Menu_Name}< :-)

   use Term::Menus;

   my @list=('One','Two','Three');

   my $result = sub {
                       my $cnt=-1;my $selection=']Selected[';
                       foreach my $item (@list) {
                          $cnt++;
                          chomp($item);
                          last if -1<index $selection, $item;
                       } return "$cnt";
                    };
                    # Anonymous subroutine assigned to "$result" variable

   my %Menu_1=(

      Item_1 => {

         Text    => "NUMBER - ]Convey[",
         Convey  => \@list,
         Result  => $result, # Anonymous subroutine assisned via
                             # "$result" variable

      },

      Select => 'One',
      Banner => "\n   Choose a /bin Utility :"
   );

   my $selection=Menu(\%Menu_1);
   print "   \nSELECTION = $selection\n";

RECURSIVELY CALLED MENUS ^

There are occasions where it is desirable to re-use the same Menu template/hash configuration with dynamically discovered data. One obvious example of this is navigating directory trees. Each subsequent directory selection could potentially contain deeper levels of directories. Essentially, any data structured in any kind of relational tree layout is subject to this kind of navigation approach. Be warned however, unlike most other functionality that is handled almost entirely by the Term::Menus module, the code for doing recursive templating is mostly contained in the template/hash configuration itself. There is a "helper routine" (&get_Menu_map) that Term::Menus provides to assist with the creation of recursively-friendly configurations, but given the highly data-centric characteristics of such functionality, most of the working code must be left to the authoring and management of the user.

&get_Menu_map()

^

This is a helper routine that returns a list of ancestor menu results. This is needed when wanting to navigate a directory tree for instance. Imagine a directory path that looks like this: /one/two/three. A call to &get_Menu_map() when processing directory three with return this list: ('one','two').

The following code is an example of how to use recursion for navigating a directory tree.

   use Term::Menus;

   my %dir_menu=(

      Name   => 'dir_menu',
      Item_1 => {

         Text => "]C[",
         Mark => "d",
         Convey => sub {

            if ("]P[") {

               my $dir="]P[";
               if ($^O eq 'cygwin') {
                  $dir='/cygdrive/c/';
               } else {
                  $dir='/';
               }
               my @xfiles=();
               my @return=();
               my @map=get_Menu_map;
               my $path=join "/", @map;
               opendir(DIR,"$dir$path") || die $!;
               @xfiles = readdir(DIR);
               closedir(DIR);
               foreach my $entry (sort @xfiles) {
                  next if $entry eq '.';
                  next if $entry eq '..';
                  if (-1<$#map) {
                     next unless -d "$dir$path/$entry";
                  } else {
                     next unless -d "$dir/$entry";
                  }
                  push @return, "$entry";
               }
               return @return;

            }
            my @xfiles=();
            my @return=();
            if ($^O eq 'cygwin') {
               opendir(DIR,'/cygdrive/c/') || die $!;
            } else {
               opendir(DIR,'/') || die $!;
            }
            @xfiles = readdir(DIR);
            closedir(DIR);
            foreach my $entry (@xfiles) {
               next if $entry eq '.';
               next if $entry eq '..';
               next unless -d "$entry";
               push @return, "$entry";
            }
            return @return;

         },
         Result => { 'dir_menu'=>'recurse' },

      },
      Item_2 => {

         Text => "]C[",
         Select => 'Many',
         Convey => sub {

            if ("]P[") {

               my $dir="]P[";
               if ($^O eq 'cygwin') {
                  $dir='/cygdrive/c/';
               } else {
                  $dir='/';
               }

               my @xfiles=();
               my @return=();
               my @map=get_Menu_map;
               my $path=join "/", @map;
               opendir(DIR,"$dir/$path") || die $!;
               @xfiles = readdir(DIR);
               closedir(DIR);
               foreach my $entry (sort @xfiles) {
                  next if $entry eq '.';
                  next if $entry eq '..';
                  if (-1<$#map) {
                     next if -d "$dir/$path/$entry";
                  } else {
                     next if -d "$dir/$entry";
                  }
                  push @return, "$entry";
               }
               return @return;

            }
            my @xfiles=();
            my @return=();
            if ($^O eq 'cygwin') {
               opendir(DIR,'/cygdrive/c/') || die $!;
            } else {
               opendir(DIR,'/') || die $!;
            }
            @xfiles = readdir(DIR);
            closedir(DIR);
            foreach my $entry (@xfiles) {
               next if $entry eq '.';
               next if $entry eq '..';
               next if -d "$entry";
               push @return, "$entry";
            }
            return @return;

         },
      },
      Banner => "   Current Directory: ]P[\n",

   );

   my $selection=Menu(\%dir_menu);

   if (ref $selection eq 'ARRAY') {
      print "\nSELECTION=",(join " ",@{$selection}),"\n";
   } else {
      print "\nSELECTION=$selection\n";
   }

USAGE and NAVIGATION ^

Usage of &pick() and/or &Menu() during the runtime of a script in which one or both are included, is simple and intuitive. Nearly everything the end user needs in terms of instruction is included on-screen. The script-writer/developer/programmer can also include whatever instructions s/he deems necessary and/or helpful in the customizable Banner (as described above). There is however, one important feature about using &Menu() with sub-menus that's important to know about.

Forward ' > ' and Backward ' < ' Navigation

^

When working with more than one &Menu() screen, it's valuable to know how to navigate back and forth between the different &Menu() levels/layers. For example, above was illustrated the output for two layers of menus - a parent and a child:

The user sees ==>

   Choose a /bin Utility :

      1.        /bin Utility - arch
      2.        /bin Utility - ash
      3.        /bin Utility - awk
      4.        /bin Utility - basename
      5.        /bin Utility - bash
      6.        /bin Utility - cat
      7.        /bin Utility - chgrp
      8.        /bin Utility - chmod
      9.        /bin Utility - chown
      10.       /bin Utility - cp

   a.  Select All   c.  Clear All   f.  FINISH
                       ___
   93 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE:

--< 5 >-<ENTER>----------------------------------

The user sees ==>

   Choose an Answer :

       1      bash is a Good Utility
       2      bash is a Bad Utility

   (Press [F1] for HELP)

   ([ESC] to Quit)   PLEASE ENTER A CHOICE:

In the above example, suppose that the user "fat-fingered" his/her choice, and really didn't want to "bash" bash, but wanted to bash awk instead. Is restarting the whole script/application now necessary? Suppose it was a process that had run overnight, and the user is seeing this menu through fogged glasses from the steam rising out of their morning coffee? Having to run the whole job again would not be welcome news for the BOSS. THANKFULLY, navigation makes this situation avoidable. All the user would have to do is type ' < ' to go backward to the previous menu, and ' > ' to go forward to the next menu (assuming there is one in each case):

The user sees ==>

   Choose an Answer :

       1      bash is a Good Utility
       2      bash is a Bad Utility

   (Press [F1] for HELP)

   ([ESC] to Quit)   PLEASE ENTER A CHOICE:

 --<  >  >-<ENTER>-----------------------------

The user sees ==>

   Choose a /bin Utility :

       1      /bin Utility - arch
       2      /bin Utility - ash
       3      /bin Utility - awk
       4      /bin Utility - basename
    -  5      /bin Utility - bash
       6      /bin Utility - cat
       7      /bin Utility - chgrp
       8      /bin Utility - chmod
       9      /bin Utility - chown
       10     /bin Utility - cp

   a.  Select All   c.  Clear All   f.  FINISH
                       ___
   93 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

Note in the above example the Dash ' - ' in front of item 5. This informs the user that s/he had previously selected this item. To clear the selection, the user would simply choose item 5 again. This effectively deletes the previous choice and restores the menu for a new selection. If the user was satisfied with the choice, and was simply double checking thier selection, they simply repeat the navigation process by typing ' > ' - then <ENTER> - and returning to the child menu they left.

If the child menu was a multiple-selection menu, and the user had made some selections before navigating back to the parent menu, the user would see a ' + ' rather than a ' - '. This informs the user that selections were made in the child menu.

   Choose a /bin Utility :

      1.        /bin Utility - arch
      2.        /bin Utility - ash
      3.        /bin Utility - awk
      4.        /bin Utility - basename
   +  5.        /bin Utility - bash
      6.        /bin Utility - cat
      7.        /bin Utility - chgrp
      8.        /bin Utility - chmod
      9.        /bin Utility - chown
      10.       /bin Utility - cp

   a.  Select All   c.  Clear All   f.  FINISH
                       ___
   93 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

View Sorted Items ' % '

^

When working with numerous items in a single menu, it may be desirable to see the set of choices organized in either descending or reverse acscii order. Term::Menus provides this feature with the Percent ' % ' key. Simply type ' % ' and the items will be sorted in descending ascii order. Type ' % ' again, and you will see the items reverse sorted. Assume that we have the following menus.

The user sees ==>

   Choose a /bin Utility :

    *  1      [.exe
    *  2      2to3
       3      2to3-3.2
    *  4      411toppm.exe
       5      a2p.exe
       6      aaflip.exe
       7      aclocal
    *  8      aclocal-1.10
       9      aclocal-1.11
    *  10     aclocal-1.12

   a.  Select All   c.  Clear All   f.  FINISH
                         ___
   1925 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

--< % >-<ENTER>----------------------------------

The user sees ==>

   Choose a /bin Utility :

   *  2.        2to3
      3.        2to3-3.2
   *  4.        411toppm.exe
      759.      FvwmCommand.exe
      1650.     Ted.exe
      1782.     WPrefs.exe
      1785.     X
      1889.     XWin.exe
      1808.     Xdmx.exe
      1815.     Xephyr.exe

   a.  Select All   c.  Clear All   f.  FINISH

   (Type '<' to return to previous Menu)
                          ___
   1925  Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

And if we choose to enter ' % ' again

--< % >-<ENTER>----------------------------------

The user sees ==>

   Choose a /bin Utility :

       1925     znew
       1924     zmore
       1923     zless
       1922     zipsplit.exe
       1921     zipnote.exe
       1920     zipinfo.exe
       1919     zipgrep
       1918     zipcloak.exe
       1917     zip.exe
       1916     zgrep

   a.  Select All   c.  Clear All   f.  FINISH

   (Type '<' to return to previous Menu)
                         ___
   1925 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP 

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

This submenu of sorted selections works just like any other menu. The user can deselect an item, clear all items, re-choose all items, etc. The choices made here are preserved when the user navigates back to the original (parent) menu. In other words, if Item 1. is deselected in the sorted menu, Item 1. will also be deselected in the parent menu. Navigating back to the parent is necessary - the menu will not generate results from a sort menu. Use either the LEFTARROW ' < ' key or FINISH key ' F or f ' to return to the parent menu, and then continue your menu activities there.

View Summary of Selected Items ' * '

^

When working with numerous items in a single menu, it is desirable to see the set of choices made before leaving the menu and committing to a non-returnable forward (perhaps even critical) process. Term::Menus provides this feature with the Star ' * ' key. Assume we have the following menu with 93 Total Choices. Assume further that we have selected items 1,3,9 & 11. Note that we cannot see Item 11 on the first screen since this menu is configured to show only 10 Items at a time.

The user sees ==>

   Choose a /bin Utility :

    *  1      [.exe
       2      2to3
    *  3      2to3-3.2
       4      411toppm.exe
       5      a2p.exe
       6      aaflip.exe
       7      aclocal
       8      aclocal-1.10
    *  9      aclocal-1.11
       10     aclocal-1.12

   a.  Select All   c.  Clear All   f.  FINISH
                         ___
   1925 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

--< * >-<ENTER>----------------------------------

The user sees ==>

   Choose a /bin Utility :

    *  1      [.exe
    *  3      2to3-3.2
    *  9      aclocal-1.11
    *  11     aclocal-1.13

   a.  Select All   c.  Clear All   f.  FINISH

   (Type '<' to return to previous Menu)

   ([F1] for HELP)

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

This submenu of summary selections works just like any other menu. The user can deselect an item, clear all items, re-choose all items, etc. The choices made here are preserved when the user navigates back to the original (parent) menu. In other words, if Item 1. is deselected in the summary menu, Item 1. will also be deselected in the parent menu. Navigating back to the parent is necessary - the menu will not generate results from a summary menu. Use either the LEFTARROW ' < ' key or FINISH key ' F or f ' to return to the parent menu, and then continue your menu activities there.

Shell Out to Command Environment ' !command '

^

Borrowed from the editor vi, users can run any command environment command (typically a shell command) without leaving their Term::Menus session or even context. At anytime, a user can type an exclamation point ' ! ' followed by the command they wish to run, and that command will be run and the results returned for viewing.

The user sees ==>

   Choose a /bin Utility :

    *  1      [.exe
       2      2to3
    *  3      2to3-3.2
       4      411toppm.exe
       5      a2p.exe
       6      aaflip.exe
       7      aclocal
       8      aclocal-1.10
    *  9      aclocal-1.11
       10     aclocal-1.12

   a.  Select All   c.  Clear All   f.  FINISH
                         ___
   1925 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE: 

--< !hostname >-<ENTER>----------------------------------

The user sees ==>

   Choose a /bin Utility :

    *  1      [.exe
       2      2to3
    *  3      2to3-3.2
       4      411toppm.exe
       5      a2p.exe
       6      aaflip.exe
       7      aclocal
       8      aclocal-1.10
    *  9      aclocal-1.11
       10     aclocal-1.12

   a.  Select All   c.  Clear All   f.  FINISH
                         ___
   1925 Total Choices   |_v_| Scroll with ARROW keys   [F1] for HELP

   ([ESC] to Quit)   PLEASE ENTER A CHOICE:

central_server

Press ENTER to continue

AUTHOR ^

Brian M. Kelly <Brian.Kelly@fullautosoftware.net>

COPYRIGHT ^

Copyright (C) 2000-2014 by Brian M. Kelly.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License. (http://www.gnu.org/licenses/agpl.html).

syntax highlighting: