-5

我正在编写本学期最后一个项目的代码,但我遇到了代码重复问题。我正在使用ncursespdcurses制作一个菜单来与用户交互。

问题:对于菜单的每个选择(总共五个)我需要一个子菜单。子菜单与主菜单的唯一区别是要打印的数组,以及由于数组大小Items而进入某些函数的参数。Items因为我需要五个子菜单,所以我需要五次相同的代码(如果添加主菜单,则需要六次)。

你们谁能帮我做一个做同样事情的函数,然后我会调用六次来创建我的菜单?

这是我的代码

void Menu(){
    const char* Items[]={
        "[1]...New tax declaration",
        "[2]...Modify tax declaration",
        "[3]...Cancel tax declaration",
        "[4]...Additional Information",
        "[5]...Exit"
    };
    int Cur=0;
    int ch, i;
    int flag=0;
    do{
        werase(wm);
        mvwaddstr(wm, 2, 16, "MENU");
        for(int i=0; i<5;i++){
            if(i==Cur)
                wattr_on(wm, A_REVERSE, 0);
            mvwaddstr(wm, 4+i, 4, Items[i]);
            wattr_off(wm, A_REVERSE, 0);
        }
        mvwaddstr(wm, 14, 3, "Choice: ");
        wprintw(wm, "%1d", Cur+1);
        wrefresh(wm);
        ch=wgetch(wm);
        switch(ch){
            case '1':Cur=0;Sub2();break;
            case '2':Cur=1;Sub1();break;
            case '3':Cur=2;break;
            case '4':Cur=3;break;
            case '5':flag=1;break;
            case KEY_UP:
            case KEY_LEFT: Cur--; if (Cur<0) Cur=4; break;
            case KEY_DOWN:
            case KEY_RIGHT: Cur++; if(Cur>4) Cur=0; break;
            case 27: flag=1; break;
            case 32:
            case 13:
                switch (Cur){
                    case 0:Sub2();break;
                    case 1:Sub1();break;
                    case 2:break;
                    case 3:break;
                    case 4:flag=1;break;
                }
        }
    }while(!flag);
}

谢谢你。

ps 代码来自一本书。我对ncurses的经验很少

4

1 回答 1

2

一个简单的菜单驱动程序。这是基于使用std::map而不是条件逻辑。该地图存储了一个结构列表,这些menuitem结构定义了菜单的外观以及每个选项的作用。

这在我们处理代码时得到了最好的解释,所以让我们开始吧!

// headers for everything used in this example
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <cctype>

// function to perform menu option B sub option 1
void optionB1()
{
    std::cout << "perfoming B1" << std::endl;
}

// function to perform menu option B sub option 2
void optionB2()
{
    std::cout << "perfoming B2" << std::endl;
}

// function to perform menu option A
void optionA()
{
    std::cout << "perfoming A" << std::endl;
}

// defines a menu item. Good naming can often save the need to comment
struct menuitem 
{
    std::function<void()> doIt; // function to run if option chosen
    std::string description; // pretty message describing option
};

// draw menu and wait for the user to select an option.
void domenu(const std::map<char, menuitem> & menu)
{
    while (true) // loop until user gives a good option. Or use a retry count. 
                 // You decide.
    {
        for (auto &items : menu)
        { // for all items in the menu, print out the item and it's description text
          // for what first and second mean, read up on std::map and std::pair
            std::cout << items.first << ") " << items.second.description << std::endl;
        }
        char ch;
        std::cin >> ch; // get the user's choice
        // often you may want to eliminate one of the cases to reduce the amount 
        // of possible inputs you need to provide handling code for.
        // the line below allows us to use the same code for input of A and a.
        ch = std::tolower(ch); // convert input to lower case
        try
        {
            menu.at(ch).doIt(); // call the function mapped to user's choice.
                                // this may do produce something or it may
                                // display another menu. It could end the wor--
            return; // done.
        }
        catch (...)
        { // print error message on unsupported input
            std::cout << "Error. Invalid option!" << std::endl;
        }
    }
}


// the B menu
std::map<char, menuitem> bmenu
{ // User input  doIt function      Description
    {'1',       {optionB1,          "Option B1"}},
    {'2',       {optionB2,          "Option B2"}}
    // add more options here. Or don't. Up to you.
};

// the main menu
std::map<char, menuitem> mainmenu
{ // User input  doIt function              Description
    {'a',       {optionA,                   "Option A"}},
    {'b',       {std::bind(domenu, bmenu),  "Option B"}}
    // OK, so that last one was a bit weird. std::bind makes a function and
    // specifies the arguments with which it will be called. This takes
    // domenu binds it with bmenu so that std::function<void()> is
    // satisfied. As far as the world is concerned, the bound function
    // returns nothing and takes no parameters. Very complicated functions 
    // can be bound so long as the end result returns nothing and requires 
    // no parameters. 
    // what it's doing here is allowing us to call domenu to draw the B 
    // submenu, wait for valid input, and call the chosen function.
};

// good 'ol trusty main so we can test that the above code isn't utter BS.
int main()
{
    while (true) // loop forever. Or use whatever exit logic is required.
    {
        domenu(mainmenu); // kick-start by calling do menu to run the main menu
    }
    return(0);
}

这将使代码保持在最低限度。所有重复的代码都被简化为domenu函数和隐藏在标准库中的大量代码,这些代码的编写者可能比你或我在正确处理这些东西方面更有经验。只要有可能,站在巨人。

domenu由选项列表和选项的执行指令驱动。想要另一种选择?将项目添加到列表中,并可能提供新功能来履行该选项的义务。

您所要做的就是填写空白。

于 2016-06-02T00:19:13.937 回答