Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

how to manage a large console menu

by paulehr (Sexton)
on May 22, 2006 at 12:43 UTC ( [id://550923]=perlquestion: print w/replies, xml ) Need Help??

paulehr has asked for the wisdom of the Perl Monks concerning the following question:

Forgive me if i am not 100% clear on my question, its Monday morning and i have not had my first cup of coffee yet ;)

I have a script i am writing that will run in a console. In one of the parts of the script I have a menu where a user will need to choose which system he wants to ftp processed files to. The menu in question is a list of 40+ systems in it and I am wondering if there was a cleaner way of displaying all the options in the console that just doing a whole mess load of print/switch statements. I have found this makes it a pain in the butt to go back and make any changes to the list (ie adding a new system) to the list.

here is some example code of how i am displaying it

#displays the menu sub menu { print "Please pick which system you wish to FTP to:\n"; print "1.) system1\n"; print "2.) system2\n"; etc.. etc.. chomp(my $option = <>); } #switch statment using switch::perlish #hostnames changed to protect the innocent while ($option ne "q") { menu(); switch $option, sub { case '1', sub {dftp("hostname_to_system1")}; case '2', sub {dftp("hostname_to_system2")}; etc etc case 'q', sub {exit}; }

Thanks for the help!

Replies are listed 'Best First'.
Re: how to manage a large console menu
by liverpole (Monsignor) on May 22, 2006 at 12:53 UTC
    If you'd like to 'roll your own', how about something like this ...?
    my @choices = ( 'system1', 'system2', 'system3', # ... ); #displays the menu sub menu { print "Please pick which system you wish to FTP to:\n"; for (my $i = 0; $i < @choices; $i++) { printf "%3d) %s\n", $i+1, $choices[$i]; } chomp(my $option = <>); }
    It would allow you to make changes to your menu choices very easily.

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/

      As paulehr made a distinction between 'system1' and 'hostname_to_system1', it might be better to use a hash:

      my %systems = ( system1 => 'hostname_to_system1', system2 => 'hostname_to_system2', ... ); my $choice = menu; dftp( $systems{$choice} ); # sub menu { my @choices = (sort keys %systems) print "Please pick which system you wish to FTP to:\n"; foreach my $i ( 0 .. $#choices ) { printf( "%2i.) %s\n", $i+1, @choices($i)); } # get input ... assuming in $option; if ( $option eq 'q' ) { exit; # or an abort routine to handle cleaning up } elsif ( defined( $choices[$option-1] ) ) { return $choices[$option-1]; } else { print "Unknown option.\nPlease try again\n"; goto &menu; # yes, goto. Please don't give me the 'goto is evil' + speach } }

      (this of course assumes the whole 'roll your own' approach, and not using some of the text-based menu systems available in CPAN)

        Yes, that's a valid point.  I considered using a hash in my example, but decided against it for the reason that, in this particular case, it's pretty easy to make the transformation from 'systemN' to 'hostname_to_systemN' for any value of N.

        The potential downside to making it a hash is that you have to sacrifice keeping it in the order you like (unless order isn't important, or you don't mind just using the order which sort generates).

        Another option which lets you keep the order you prefer, and still have corresponding values for each of your "keys" would be to have both an array *and* a hash, eg.:

        my @systems = qw( this_system_should_be_first system1 system2 system3 the_final_system ); my %systems = ( this_system_should_be_first => 'hostname_to_first_system', system1 => 'hostname_to_system1', system2 => 'hostname_to_system2', system3 => 'hostname_to_system3', the_final_system => 'hostname_to_final_system', );
        which gives a nice flexibility, but requires making updates in two data structures rather than just one.

        The tradeoff one chooses will ultimately be a matter of personal preference and/or need.


        s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: how to manage a large console menu
by dorward (Curate) on May 22, 2006 at 12:47 UTC

    Curses::UI::Popupmenu might be the way to go.

    I haven't used it, but it looks like you should be able to use the onChange method to read the value of the selected item and pass that to your subroutine.

Re: how to manage a large console menu
by MonkE (Hermit) on May 22, 2006 at 13:43 UTC
    For even more maintainability try this extension of the menu code from above (in Re: how to manage a large console menu). It stores the sytem name, and the ftp hostname in a single hash. Sub menu automatically sorts and numbers your choices, so there is no need to painstakingly renumber every time you insert a new host.
    %choices = ( "system1" => "hostname_for_system1", "system2" => "hostname_for_system2", "system3" => "hostname_for_system3", ); #displays the menu sub menu { my $i = 0; my @sorted_keys = sort(keys %choices); print "Please pick which system you wish to FTP to:\n"; foreach $key (@sorted_keys) { printf "%3d) %s\n", ++$i, $key; } chomp(my $option = <>); return $choices{$sorted_keys[$option-1]} if $option >= 1 && $optio +n <= scalar(@sorted_keys); } $pick = menu; print "system picked = $pick\n"; # do_something_with($pick);
Re: how to manage a large console menu
by ioannis (Abbot) on May 22, 2006 at 13:07 UTC
    If you can spare a few hours installing and learning, I would apt for CursesWidgets, But, if you want a working menu within minutes, the Perl equivalent to select(1) is packaged through Shell::POSIX::Select .
Re: how to manage a large console menu
by TedPride (Priest) on May 22, 2006 at 18:15 UTC
    use strict; use warnings; my @hosts = ( { 'name' => 'Site 1', 'host' => 'hostname1' }, { 'name' => 'Site 2', 'host' => 'hostname2' }, { 'name' => 'Site 3', 'host' => 'hostname3' }, ## Etc... ); while (1) { print "Please pick which system you wish to FTP to:\n"; print $_+1, ') ', $hosts[$_]{'name'}, "\n" for 0..$#hosts; print "Q) Quit\n\n"; chomp($_ = <STDIN>); last if uc($_) eq 'Q'; dftp($hosts[$_-1]{'host'}); print "\n"; }
    Since you probably want them in a specific order, the outer structure should be an array. Since you might want to add more data about each item in the future, and don't want to have to worry about order, the inner structure should be a hash.
Re: how to manage a large console menu
by paulehr (Sexton) on May 22, 2006 at 13:58 UTC

    Thanks for all the suggestions. Would the curses stuff work with an WinXP workstation?, i didnt think windows can do that. I think going the hash route will be the best way to go on this one.

      Hi,

      I think only curses has been ported to win32. To the best of my knowledge non of the curses extensions have been ported yet (or even if they will be).

      Displeaser

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://550923]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (4)
As of 2024-04-20 00:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found