Your Ad Here
首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 操作系统 > Linux > 第十七章 菜单库
【标  题】:第十七章 菜单库
【关键字】:
【来  源】:http://blog.csdn.net/byronm/archive/2006/10/03/1319936.aspx

第十七章 菜单库

Your Ad Here

第十七章 菜单库

菜单库对cureses基础库进行了很好的扩展。你可以通过这个库所提供的函数方便的创建菜单。如果你想让它更美观,可以定制它的显示效果。下面我们就来看看这个库。

 

菜单是一个用来帮助用户选择子菜单项的屏幕。简而言之,菜单就是一个菜单项的集合,使你可以方便的从中选择相应的菜单命令。curses菜单库还提供编制多选菜单的功能。有些读者可能不了解多选菜单。这个我们稍后讨论,我们先来了解一下菜单库的基础知识。

171 基础知识

要创建菜单,你首先要建立菜单项,然后发送并显示菜单。接下来,所有处理用户响应的工作就交给功能强大的menu_driver()函数来完成。这个函数是整个菜单库的核心。

一个菜单程序大致的控制流程如下:

1. 初始化curses

2. 用函数new_item()创建菜单项,同时为菜单项指定名称并且描述其相应的功能。

3. 用函数new_menu()创建菜单,同时指定要添加的菜单项。

4. 用函数post_menu()递送菜单并刷新屏幕

5. 用一个循环处理用户的菜单请求。并用menu_driver()函数对菜单做必要的更新。

6. unpost_menu()取消菜单递送。

7. free_menu()释放分配给菜单的内存

8. free_item()释放分配给菜单项的内存

9. 结束curses

现在我们看一个简单菜单的示例程序,它是用方向键来更新当前菜单项的。

 

172 编译包含菜单库的程序

要使用菜单库中的函数,首先要把menu.h文件包含进去。编译并连接时要同时添加-lmenu-lncurses两个参数。

  #include <menu.h>

    .

    .

.

编译和连接: gcc <程序> -lmenu –lncurses

 

18 菜单基础知识示例

#include <curses.h>

#include <menu.h>

 

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

#define CTRLD 4

char *choices[] = {

                        "Choice 1",

                        "Choice 2",

                        "Choice 3",

                        "Choice 4",

                        "Exit",

                  };

int main()

{      ITEM **my_items;

       int c;                           

       MENU *my_menu;

       int n_choices, i;

       ITEM *cur_item;

      

       initscr();

       cbreak();

       noecho();

       keypad(stdscr, TRUE);

       n_choices = ARRAY_SIZE(choices);

       my_items = (ITEM **)calloc(n_choices + 1, sizeof(ITEM *));

 

       for(i = 0; i < n_choices; ++i)

               my_items[i] = new_item(choices[i], choices[i]);

       my_items[n_choices] = (ITEM *)NULL;

 

       my_menu = new_menu((ITEM **)my_items);

       mvprintw(LINES - 2, 0, "F1 to Exit");

       post_menu(my_menu);

       refresh();

       while((c = getch()) != KEY_F(1))

       {   switch(c)

           {       case KEY_DOWN:

                      menu_driver(my_menu, REQ_DOWN_ITEM);

                            break;

                     case KEY_UP:

                            menu_driver(my_menu, REQ_UP_ITEM);

                            break;

              }

       }     

       free_item(my_items[0]);

       free_item(my_items[1]);

       free_menu(my_menu);

       endwin();

}

这个程序演示了用菜单库创建菜单的基本步骤。首先用new_item()函数建立菜单项,然后用new_menu()函数把这些菜单项添加到菜单。当递送了菜单并刷新屏幕后,主循环就开始处理。它读取用户的输入并进行相应的操作。函数menu_driver()是菜单系统的核心函数。这个函数的第二个参数是相应菜单操作宏。menu_driver()函数根据参数执行相应的操作。参数的值可以是菜单的导航请求、一个ASCII码或与鼠标事件相关的一个特定KEY_MOUSE值。

 

menu_driver()函数可以接受以下导览请求:(就是第二个参数)

REQ_LEFT_ITEM         左移一个菜单项。

REQ_RIGHT_ITEM              右移一个菜单项。

REQ_UP_ITEM                     上移一个菜单项。

REQ_DOWN_ITEM             下移一个菜单项。

REQ_SCR_ULINE          向上滚动一行。

REQ_SCR_DLINE         向下滚动一行。

REQ_SCR_DPAGE              下翻一页。

REQ_SCR_UPAGE        上翻一页

REQ_FIRST_ITEM              跳到首项。

REQ_LAST_ITEM         跳到最末一项。

REQ_NEXT_ITEM         跳到下一项。

REQ_PREV_ITEM         跳到上一项。

REQ_TOGGLE_ITEM        选择/取消选择一项。

REQ_CLEAR_PATTERN       清空菜单模式缓冲区。

REQ_BACK_PATTERN       删除菜单模式缓冲区的前面一个字符。

REQ_NEXT_MATCH            跳到下一个与模式匹配的项。

REQ_PREV_MATCH            跳到上一个与模式匹配的项。

千万不要被这么多的操作请求吓倒,稍后我们会一个一个地讲解。在这个例子中,最有趣的是REQ_UP_ITEMREQ_DOWN_ITEM。当这两个选项传给menu_driver()函数时,menu_driver()函数将会通过重新刷新屏幕上移或下移一个菜单项。

173 Menu Driver:菜单系统的核心

如你在上面的例子中所看到的,menu_driver在更新菜单时有着举足轻重的作用。所以了解它的各个选项和它们的作用就很有必要了。前面已经解释过,menu_driver()的第二个参数可以是一个导航请求。一个可打印的字符(ASCII码)或KEY_MOUSE键值。我们来剖析一下各个导航请求:

 

REQ_LEFT_ITEM REQ_RIGHT_ITEM

一个菜单可以用多列的方式显示菜单项,这可以用函数menu_format()来实现。当显示一个多列菜单时,这两个移动请求可使menu drive()在当前菜单项位置左移或右移一个菜单项。

 

REQ_UP_ITEM REQ_DOWN_ITEM

这两个移动请求在上面的例子中已经出现过。将这两个移动请求传递给menu_driver时,menu_driver()从当前菜单项上移或下移一个菜单项。

 

REQ_SCR_系列的移动请求

REQ_SCR_ULINE, REQ_SCR_DLINE, REQ_SCR_DPAGE REQ_SCR_UPAGE是有关屏幕滚动的请求。如果所有的菜单项在当前菜单子窗口中显示不完,菜单就是可滚动的。当这些请求传给menu_driver时,将分别向上/下滚动一行/页。

 

REQ_FIRST_ITEM, REQ_LAST_ITEM, REQ_NEXT_ITEM REQ_PREV_ITEM

这些请求的功能从名字上就可以明显的看出来。分别是:移动到第一个菜单项、最后一个菜单项、下一个菜单项和前一个菜单项。

 

REQ_TOGGLE_ITEM

当使用这个移动请求时,当前菜单项会被锁定。这个选项仅用于一个多层菜单。因此使用它时须关闭O_ONEVALUE(通过set_menu_opts()函数关闭或启用)。

 

样式匹配请求

每个菜单都有一个与之关联的样式匹配缓冲区。通过这个缓冲区,用户可以通过输入ASCII字符串查找与之匹配的菜单项。任何传给menu_driverASCII字符都会被送入样式匹配缓冲区,它同时试着从菜单项列表中查找与之最近的匹配项。然后立刻从当前菜单项跳转到与匹配样式最近的菜单项上去。REQ_CLEAR_PATTERN请求用来清空匹配缓冲区。REQ_BACK_PATTERN清除模式缓冲区中前一个匹配样式。如果有多个匹配样式,这些匹配样式就可以通过REQ_NEXT_PATTERNREQ_PREV_PATTERN进行切换,这两个选项分别从当前菜单项移至下或上一个匹配样式。

 

鼠标事件请求

如果有鼠标事件请求,则根据鼠标的位置来产生相应的事件。在man帮助页中是这样解释这些行为的:

如果第二个参数是KEY_MOUSE的键值,相应的鼠标事件就会转化为上面已经定义的请求。现在你只须在用户窗口点击鼠标(如:在菜单显示区或窗口)。如果你是在菜单显示区单击的,将生成REQ_SCR_ULINE选项,如果是双击,就会生成REQ_SCR_UPAGE,如果单击三次,则生成REQ_FIRST_ITEM。如果你是在菜单显示区域的下方单击的,将生成REQ_SCR_DLINE,若是双击,就会生成REQ_SCR_DPAGE,若是单击三次的话,则生成REQ_LAST_ITEM。如果你单击菜单显示区内的某个菜单项,菜单的提示光标就定位在那个菜单项上了。

后面的程序中将会对以上的导航请求作详细的讲解。

174 含菜单窗口

每个已创建的菜单都对应着一个窗口和一个子窗口。菜单窗口显示菜单的标题或边框线。菜单子窗口显示当前可选的菜单项。而在上面的例子里我们并没有指定窗口或子窗口。当窗口未被指定时,stdscr将作为菜单窗口。然后菜单系统根据将要显示的菜单项计算子窗口的大小。菜单项就在这些规划好的子窗口中显示出来。让我们利用这些窗口,来打印一个有边框线和标题的菜单

 

19 一个菜单窗口用法的例子

#include <menu.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

#define CTRLD 4

 

char *choices[] = {

                        "Choice 1",

                        "Choice 2",

                        "Choice 3",

                        "Choice 4",

                        "Exit",

                        (char *)NULL,

                  };

void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);

 

int main()

{      ITEM **my_items;

       int c;                           

       MENU *my_menu;

    WINDOW *my_menu_win;

    int n_choices, i;

      

       /* 初始化curses */

       initscr();

       start_color();

    cbreak();

    noecho();

       keypad(stdscr, TRUE);

       init_pair(1, COLOR_RED, COLOR_BLACK);

 

       /* 创建菜单项 */

    n_choices = ARRAY_SIZE(choices);

    my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));

    for(i = 0; i < n_choices; ++i)

            my_items[i] = new_item(choices[i], choices[i]);

 

       /* 创建菜单 */

       my_menu = new_menu((ITEM **)my_items);

 

       /* 创建与菜单相关联的窗口*/

    my_menu_win = newwin(10, 40, 4, 4);

    keypad(my_menu_win, TRUE);

 

       /* 设置主窗口和子窗口 */

    set_menu_win(my_menu, my_menu_win);

    set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));

 

       /* 设置字符串的标记为 " * " */

    set_menu_mark(my_menu, " * ");

 

       /* 在主窗口的边界打印边框线和标题 */

    box(my_menu_win, 0, 0);

       print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));

       mvwaddch(my_menu_win, 2, 0, ACS_LTEE);

       mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);

       mvwaddch(my_menu_win, 2, 39, ACS_RTEE);

       mvprintw(LINES - 2, 0, "F1 to exit");

       refresh();

       

       /* 递送菜单 */

       post_menu(my_menu);

       wrefresh(my_menu_win);

 

       while((c = wgetch(my_menu_win)) != KEY_F(1))

       {       switch(c)

               {       case KEY_DOWN:

                            menu_driver(my_menu, REQ_DOWN_ITEM);

                            break;

                            case KEY_UP:

                            menu_driver(my_menu, REQ_UP_ITEM);

                            break;

                     }

            wrefresh(my_menu_win);

       }     

 

       /* 取消递送并释放占用的内存 */

    unpost_menu(my_menu);

    free_menu(my_menu);

    for(i = 0; i < n_choices; ++i)

    free_item(my_items[i]);

       endwin();

}

 

void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color)

{      int length, x, y;

       float temp;

 

       if(win == NULL)

              win = stdscr;

       getyx(win, y, x);

       if(startx != 0)

              x = startx;

       if(starty != 0)

              y = starty;

       if(width == 0)

              width = 80;

 

       length = strlen(string);

       temp = (width - length)/ 2;

       x = startx + (int)temp;

       wattron(win, color);

       mvwprintw(win, y, x, "%s", string);

       wattroff(win, color);

       refresh();

}

这个例子创建了这样一个菜单:有标题、边框,以及一根用来分隔开标题和菜单项的线。如你所见,使用set_menu_win()函数把菜单附加到一个窗口上,之后使用set_menu_sub()函数把菜单的子窗口也附加到这个窗口上,菜单项就可以在子窗口中显示。使用set_menu_mark()函数可以来设置标志串,标志串就会出现在所选菜单项的左边。

17.5 滚动菜单

如果设置的子窗口不够显示所有的菜单项,菜单将变成可滚动的。在当前列的最后一个菜单项处传递REQ_DOWN_ITEM参数,它将会变成REQ_SCR_DLINE,并滚动到下一个菜单项。你也可以手动的用REQ_SCR_操作来滚动菜单。现在让我们来看看怎样实现菜单滚动。

20.一个滚动菜单的例子

#include <curses.h>

#include <menu.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

#define CTRLD 4

char *choices[] = {

                        "Choice 1",

                        "Choice 2",

                        "Choice 3",

                        "Choice 4",

                                          "Choice 5",

                                          "Choice 6",

                                          "Choice 7",

                                          "Choice 8",

                                          "Choice 9",

                                          "Choice 10",

                        "Exit",

                        (char *)NULL,

                  };

void print_in_middle(WINDOW *win, int starty, int startx, int width, char *string, chtype color);

 

int main()

{      ITEM **my_items;

       int c;                           

       MENU *my_menu;

        WINDOW *my_menu_win;

        int n_choices, i;

      

       /* 初始化curses */

       initscr();

       start_color();

    cbreak();

    noecho();

       keypad(stdscr, TRUE);

       init_pair(1, COLOR_RED, COLOR_BLACK);

       init_pair(2, COLOR_CYAN, COLOR_BLACK);

 

       /* 创建菜单项 */

    n_choices = ARRAY_SIZE(choices);

    my_items = (ITEM **)calloc(n_choices, sizeof(ITEM *));

    for(i = 0; i < n_choices; ++i)

           my_items[i] = new_item(choices[i], choices[i]);

 

       /* 创建菜单 */

       my_menu = new_menu((ITEM **)my_items);

 

       /* 创建与菜单相关联的窗口 */

    my_menu_win = newwin(10, 40, 4, 4);

    keypad(my_menu_win, TRUE);

    

       /* 设置主窗口和子窗口 */

    set_menu_win(my_menu, my_menu_win);

    set_menu_sub(my_menu, derwin(my_menu_win, 6, 38, 3, 1));

       set_menu_format(my_menu, 5, 1);

                    

       /* 设置标志串为" * " */

    set_menu_mark(my_menu, " * ");

 

       /* 在主窗口的边界打印边框线和标题 */

    box(my_menu_win, 0, 0);

       print_in_middle(my_menu_win, 1, 0, 40, "My Menu", COLOR_PAIR(1));

       mvwaddch(my_menu_win, 2, 0, ACS_LTEE);

       mvwhline(my_menu_win, 2, 1, ACS_HLINE, 38);

       mvwaddch(my_menu_win, 2, 39, ACS_RTEE);

       

       /* 传递菜单 */

       post_menu(my_menu);

       wrefresh(my_menu_win);

      

       attron(COLOR_PAIR(2));

       mvprintw(LINES - 2, 0, "Use PageUp and PageDown to scoll down or up a page of items");

       mvprintw(LINES - 1, 0, "Arrow Keys to navigate (F1 to Exit)");

       attroff(COLOR_PAIR(2));

       refresh();

 

       while((c = wgetch(my_menu_win)) != KEY_F(1))

       {       switch(c)

               {       case KEY_DOWN:

   &