从菜单中有条理地排除菜单选项

时间:2015-02-17 15:20:21

标签: perl user-interface menu conditional-statements submenu

我编写了一个可以构建简单菜单并管理它们的Perl模块,但是现在我需要弄清楚当我不希望它们可用时如何有条件地隐藏菜单选项。

例如,如果满足特定条件,如何在"Choice2"中隐藏$menu1

这个问题在某种程度上延续了我的其他一个问题: How can I build a simple menu in Perl?

自从我开始这项工作以来,我取得了相当大的进步,但我似乎遇到了障碍。

菜单模块如下所示:

# Menu.pm

#!/usr/bin/perl

package Menu;

use strict;
use warnings;

# Menu constructor
sub new {

    # Unpack input arguments
    my $class       = shift;
    my (%args)      = @_;
    my $title       = $args{title};
    my $choices_ref = $args{choices};
    my $noexit      = $args{noexit};

    # Bless the menu object
    my $self = bless {
        title   => $title,
        choices => $choices_ref,
        noexit  => $noexit,
    }, $class;

    return $self;
}

# Print the menu
sub print {

    # Unpack input arguments
    my $self    = shift;
    my $title   =   $self->{title  };
    my @choices = @{$self->{choices}};
    my $noexit  =   $self->{noexit };

    # Print menu
    for (;;) {

        # Clear the screen
        system 'cls';

        # Print menu title
        print "========================================\n";
        print "    $title\n";
        print "========================================\n";

        # Print menu options
        my $index = 0;
        for my $choice(@choices) {
            printf "%2d. %s\n", ++$index, $choice->{text};
        }
        printf "%2d. %s\n", '0', 'Exit' unless $noexit;

        print "\n?: ";

        # Get user input
        chomp (my $input = <STDIN>);

        print "\n";

        # Process input
        if ($input =~ m/\d+/ && $input >= 1 && $input <= $index) {
            return $choices[$input - 1]{code}->();
        } elsif ($input =~ m/\d+/ && !$input && !$noexit) {
            print "Exiting . . .\n";
            exit 0;
        } else {
            print "Invalid input.\n\n";
            system 'pause';
        }
    }
}

1;

以下是如何使用该模块的示例:

# test.pl

#!/usr/bin/perl

use strict;
use warnings;

use Menu;

my $menu1;
my $menu2;

# define menu1 choices
my @menu1_choices = (
    { text => 'Choice1',
      code => sub { print "I did something!\n"; }},
    { text => 'Choice2',
      code => sub { print "I did something else!\n"; }},
    { text => 'Go to Menu2',
      code => sub { $menu2->print(); }},
);

# define menu2 choices
my @menu2_choices = (
    { text => 'Choice1',
      code => sub { print "I did something in menu 2!\n"; }},
    { text => 'Choice2',
      code => sub { print "I did something else in menu 2!\n"; }},
    { text => 'Go to Menu1',
      code => sub { $menu1->print(); }},
);

# Build menu1
$menu1 = Menu->new(
    title   => 'Menu1',
    choices => \@menu1_choices,
);

# Build menu2
$menu2 = Menu->new(
    title   => 'Menu2',
    choices => \@menu2_choices,
);

# Print menu1
$menu1->print();

由于菜单选项被定义为哈希数组,因此如果我不想让它们显示,我不确定如何有条件地排除特定选项。

有一种简单的方法吗?

4 个答案:

答案 0 :(得分:2)

您可以创建一个MenuItem包,然后在选项中设置一个标志,以决定是否应该包含它。下面是创建菜单选项时使用新包的完整代码集。为了演示它,在第一个菜单中为第二个选项设置了'禁用'标志。

请注意,在计算用户的响应时,“print”子例程中添加了一些额外的代码来处理禁用的选项。

#!/usr/bin/perl

package MenuItem;

use strict;
use warnings;

sub new {
    # Unpack input arguments
    my $class       = shift;
    my (%args)      = @_;
    my $text        = $args{text};
    my $code        = $args{code};
    my $disabled        = $args{disabled};

    # Bless the menu object
    my $self = bless {
        text   => $text,
        code   => $code,
        disabled => $disabled,
    }, $class;

    return $self;
}

1;

package Menu;

use strict;
use warnings;

# Menu constructor
sub new {

    # Unpack input arguments
    my $class       = shift;
    my (%args)      = @_;
    my $title       = $args{title};
    my $choices_ref = $args{choices};
    my $noexit      = $args{noexit};

    # Bless the menu object
    my $self = bless {
        title   => $title,
        choices => $choices_ref,
        noexit  => $noexit,
    }, $class;

    return $self;
}

# Print the menu
sub print {

    # Unpack input arguments
    my $self    = shift;
    my $title   =   $self->{title  };
    my @choices = @{$self->{choices}};
    my $noexit  =   $self->{noexit };

    # Print menu
    for (;;) {

        # Clear the screen
        system 'cls';

        # Print menu title
        print "========================================\n";
        print "    $title\n";
        print "========================================\n";

        # Print menu options
        my $index = 0;
    my @items;
        for my $choice(@choices) {
        if ( ! $choice->{disabled} ) {
        $items[$index]=$choice;
        printf "%2d. %s\n", ++$index, $choice->{text};
        }
        }
        printf "%2d. %s\n", '0', 'Exit' unless $noexit;

        print "\n?: ";

        # Get user input
        chomp (my $input = <STDIN>);

        print "\n";

        # Process input
        if ($input =~ m/\d+/ && $input >= 1 && $input <= $index) {
            return $items[$input - 1]->{code}->();
        } elsif ($input =~ m/\d+/ && !$input && !$noexit) {
            print "Exiting . . .\n";
            exit 0;
        } else {
            print "Invalid input.\n\n";
            system 'pause';
        }
    }
}

1;

use strict;
use warnings;

#use Menu;

my $menu1;
my $menu2;

# define menu1 choices
my @menu1_choices = (
    MenuItem->new(text => 'Choice1',
          code => sub { print "I did something!\n"; }),
    MenuItem->new(text => 'Choice2',
          code => sub { print "I did something else!\n"; },
          disabled => 1),
    MenuItem->new(text => 'Go to Menu2',
          code => sub { $menu2->print(); }),
);

# define menu2 choices
my @menu2_choices = (
    MenuItem->new(text => 'Choice1',
          code => sub { print "I did something in menu 2!\n"; }),
    MenuItem->new(text => 'Choice2',
          code => sub { print "I did something else in menu 2!\n"; }),
    MenuItem->new(text => 'Go to Menu1',
          code => sub { $menu1->print(); }),
);

# Build menu1
$menu1 = Menu->new(
    title   => 'Menu1',
    choices => \@menu1_choices,
);

# Build menu2
$menu2 = Menu->new(
    title   => 'Menu2',
    choices => \@menu2_choices,
);

# Print menu1
$menu1->print();

答案 1 :(得分:1)

我不清楚你在问什么。我想你在问“鉴于我有一系列哈希,我怎么能忽略那些包含特定键的哈希呢?”

创建Menu对象时,您可以使用grep语句轻松完成此操作:

my $menu2 = Menu->new(
    title   => 'Menu2',
    choices => [grep { $_->{text} ne 'Choice2' } @menu2_choices],
);

答案 2 :(得分:1)

我们假设有一个黑名单作为参数。当然你可以把它放在其他地方,例如作为对象的属性。

只需检查每个黑名单。或者直接从选择数组中删除它们。

sub print {
  my ($self, @blacklist) = @_;
    for my $choice (@choices) {
       printf "%2d. %s\n", ++$index, $choice->{text}
         unless grep { $_ eq $choice->{text} } @blacklist;
    }
}

答案 3 :(得分:0)

感谢lovedatsnow将这些物品作为自己的物品!我基本上接受了你的回答并对其进行了修改,使创建菜单的界面看起来比以前更清晰。

这是我的新代码:

# test.pl

#!/usr/bin/perl

# Always use these
use strict;
use warnings;

# Other use statements
use Menu;

# Create a menu object
my $menu = Menu->new();

# Add a menu item
$menu->add(
    'Test'  => sub { print "This is a test\n";  system 'pause'; },
    'Test2' => sub { print "This is a test2\n"; system 'pause'; },
    'Test3' => sub { print "This is a test3\n"; system 'pause'; },
);

# Disable a menu item
$menu->disable('Test2');
$menu->print();

# Enable a menu item
$menu->enable('Test2');
$menu->print();

我创建了一个菜单类,其中包含一些有用的功能,允许您直接对菜单项执行操作。这允许您轻松启用/禁用它们。

# Menu.pm

#!/usr/bin/perl

package Menu;

# Always use these
use strict;
use warnings;

# Other use statements
use Carp;
use Menu::Item;

# Menu constructor
sub new {

    # Unpack input arguments
    my ($class, $title) = @_;

    # Define a default title
    if (!defined $title) {
        $title = 'MENU';
    }

    # Bless the Menu object
    my $self = bless {
        _title => $title,
        _items => [],
    }, $class;

    return $self;
}

# Title accessor method
sub title {
    my ($self, $title) = @_;
    $self->{_title} = $title if defined $title;
    return $self->{_title};
}

# Items accessor method
sub items {
    my ($self, $items) = @_;
    $self->{_items} = $items if defined $items;
    return $self->{_items};
}

# Add item(s) to the menu
sub add {

    # Unpack input arguments
    my ($self, @add) = @_;
    croak 'add() requires name-action pairs' unless @add % 2 == 0;

    # Add new items
    while (@add) {
        my ($name, $action) = splice @add, 0, 2;

        # If the item already exists, remove it
        for my $index(0 .. $#{$self->{_items}}) {
            if ($name eq $self->{_items}->[$index]->name()) {
                splice @{$self->{_items}}, $index, 1;
            }
        }

        # Add the item to the end of the menu
        my $item = Menu::Item->new($name, $action);
        push @{$self->{_items}}, $item;
    }

    return 0;
}

# Remove item(s) from the menu
sub remove {

    # Unpack input arguments
    my ($self, @remove) = @_;

    # Remove items
    for my $name(@remove) {

        # If the item exists, remove it
        for my $index(0 .. $#{$self->{_items}}) {
            if ($name eq $self->{_items}->[$index]->name()) {
                splice @{$self->{_items}}, $index, 1;
            }
        }
    }

    return 0;
}

# Disable item(s)
sub disable {

    # Unpack input arguments
    my ($self, @disable) = @_;

    # Disable items
    for my $name(@disable) {

        # If the item exists, disable it
        for my $index(0 .. $#{$self->{_items}}) {
            if ($name eq $self->{_items}->[$index]->name()) {
                $self->{_items}->[$index]->active(0);
            }
        }
    }

    return 0;
}

# Enable item(s)
sub enable {

    # Unpack input arguments
    my ($self, @enable) = @_;

    # Disable items
    for my $name(@enable) {

        # If the item exists, enable it
        for my $index(0 .. $#{$self->{_items}}) {
            if ($name eq $self->{_items}->[$index]->name()) {
                $self->{_items}->[$index]->active(1);
            }
        }
    }
}

# Print the menu
sub print {

    # Unpack input arguments
    my ($self) = @_;

    # Print the menu
    for (;;) {
        system 'cls';

        # Print the title
        print "========================================\n";
        print "    $self->{_title}\n";
        print "========================================\n";

        # Print menu items
        for my $index(0 .. $#{$self->{_items}}) {
            my $name   = $self->{_items}->[$index]->name();
            my $active = $self->{_items}->[$index]->active();
            if ($active) {
                printf "%2d. %s\n", $index + 1, $name;
            } else {
                print "\n";
            }
        }
        printf "%2d. %s\n", 0, 'Exit';

        # Get user input
        print "\n?: ";
        chomp (my $input = <STDIN>);

        # Process user input
        if ($input =~ m/\d+/ && $input > 0 && $input <= scalar @{$self->{_items}}) {
            my $action = $self->{_items}->[$input - 1]->action();
            my $active = $self->{_items}->[$input - 1]->active();
            if ($active) {
                print "\n";
                return $action->();
            }
        } elsif ($input =~ m/\d+/ && $input == 0) {
            return 0;
        }

        # Deal with invalid input
        print "\nInvalid input.\n\n";
        system 'pause';
    }
}

1;

最后,我创建了Menu :: Item类,允许您创建单个项目并存储每个项目的必要信息。 (请注意,它存储在名为&#34;菜单&#34;的文件夹中,以便引用正常工作。)

# Item.pm

#!/usr/bin/perl

package Menu::Item;

# Always use these
use strict;
use warnings;

# Menu::Item constructor
sub new {

    # Unpack input arguments
    my ($class, $name, $action) = @_;

    # Bless the Menu::Item object
    my $self = bless {
        _name   => $name,
        _action => $action,
        _active => 1,
    }, $class;

    return $self;
}

# Name accessor method
sub name {
    my ($self, $name) = @_;
    $self->{_name} = $name if defined $name;
    return $self->{_name};
}

# Action accessor method
sub action {
    my ($self, $action) = @_;
    $self->{_action} = $action if defined $action;
    return $self->{_action};
}

# Active accessor method
sub active {
    my ($self, $active) = @_;
    $self->{_active} = $active if defined $active;
    return $self->{_active};
}

1;

这为构建和使用菜单创建了一个非常优雅的界面!

没有更丑陋的哈希数组。 :)