⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch17.htm

📁 ncurses中文说明
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<html>
<head>
<title>第十七章:菜单库</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="keywords" content="ncurses,curses,扩展库">
</head>
<body leftmargin=40 bgcolor="#DAFFFF" text="black" link="#0000ff" alink="blue" vlink="#940084">
<br>
<div align="center"><a href="index.htm"><font size="5">
    <strong>
    回  目  录
    </strong>
  </font></a></div>
  <div align="right">
  <font size="5">
  <a href="ch16.htm">
  <strong>
  上 一 章
  </strong>
  </a>
  <a href="ch18.htm">
    <strong>
    下 一 章
	</strong>
	</a>
	</font>
	</div>
	<br>
<br>
<br>
<h1 align="center">
第十七章:菜单库
</h1>
<br>
<br>
<br>
<div align="left">
<font size="4">
<p>
<dd>
菜单库(Menu)很好的扩展了cureses基础库。你可以通过这个库所提供的函数方便的创建菜单。同时你一定会定制它的显示效果,让它看起来更美观。那么,我们来看看这个库。
</dd>
</p>
<p>
<dd>>
菜单就是一个显示屏幕,里面包含着帮助用户选择的特定子菜单项。简而言之,菜单就是一个菜单项的集合,使你可以方便的从中选择相应的菜单命令。有些读者还不了解多选菜单的强大功能。菜单库提供编制多选菜单的功能。这个我们稍后讨论,现在我们了解一下菜单库的基础知识。
</dd>
</p>
<h3 align="left">
<a name="basis">
17.1 基础知识
</a>
</h3>
<p>
<dd>
要创建菜单,首先要建立菜单项,之然后通过菜单把菜单项显示出来。接下来,就由功能强大的menu_driver()函数来处理用户的操作。对于处理包含菜单的程序来说,这个函数是一个非常强大的工具。
</dd>
</p>
<p>
一个菜单程序的控制流程大致如下:
</p>
<pre>
1.	初始化curses
2.	用函数new_item()创建菜单项。同时要为菜单项指定名称用来描述相应功能。
3.	用函数new_menu()创建菜单,同时指定要添加的菜单项。
4.	用函数menu_post()递送菜单并刷新屏幕
5.	用一个循环处理用户的菜单请求。并用menu_driver()函数对菜单做必要的更新。
6.	用menu_unpost()取消菜单递送。
7.	用free_menu()释放分配给菜单的内存
8.	用free_item()释放分配给菜单项的内存
9.	结束curses

</pre>
<p>
<dd>
现在我们看一个简单的菜单示例程序,这个程序可以用方向键来更新当前菜单项。
</dd>
</p>
<br>
<br>
<h3 align="left">
<a name="ch17.htm#compile">
17.2 编译包含菜单库的程序
</a>
</h3>
<br>
<p>
<dd>
要使用菜单库中的函数,首先要把menu.h这个文件包含进去。在编译和连接时就要同时添加-lmenu和-lncurses两个参数。
</dd>
</p>
<font color="Maroon">
<pre>
	#include &lt;menu.h&gt;

编译和连接: gcc &lt;程序&gt; -lmenu –lncurses
</pre>
</font>
<p>
例18 菜单基础知识示例
</p>
<font color="Maroon">
<pre>
#include &lt;curses.h&gt;
#include &lt;menu.h&gt;

#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 &lt; 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();
}

</pre>
</font>
<p>
<dd>
这个程序演示了用菜单库创建菜单的基本步骤。首先我们用new_item()建立菜单项,然后用new_menu()函数把它们添加到菜单。当递送菜单并刷新屏幕后,主循环就开始处理了。它读入取用户的输入并进行相应的操作。函数menu_driver()是处理菜单系统的主函数。这个函数的第二个参数用来说明每个菜单的相应操作。menu_driver()函数根据参数执行相应的任务。参数的值可以是菜单导览请求,一个ASCII码或与鼠标事件相关的一个特定的KEY_MOUSE值。
</dd>
</p>
<pre>
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			跳到上一个与模式匹配的项。

</pre>
<p>
<dd>
千万不要被这么多的操作请求吓倒,稍后我们会一个一个地讲解。在这个例子中,最有趣的是REQ_UP_ITEM和REQ_DOWN_ITEM。当这两个选项传给menu_driver()函数时,menu_driver()函数将会通过重新刷新屏幕上移或下移一个菜单项。
</dd>
</p>
<br>
<br>
<h3 align="left">
<a name="menudriver">
17.3 Menu Driver:菜单系统的灵魂
</a>
</h3>
<br>
<p>
<dd>
如你在上面的例子中所看到的,menu_driver在更新菜单时有着举足轻重的作用。所以了解它的各个选项和它们的作用就很有必要了。前面已经解释了,menu_driver()的第二个参数可以是一个移动请求,一个可打印的字符或KEY_MOUSE键值。我们来剖析一下各个移动请求的区别。
</dd>
</p>
<p>
REQ_LEFT_ITEM 和 REQ_RIGHT_ITEM
</p>
<p>
<dd>
一个菜单可以用多列的方式显示菜单项,这可以用函数menu_format()来实现。当显示一个多列菜单时,这两个移动请求可使menu drive在当前菜单项位置左移或右移一个菜单项。
</dd>
</p>
<p>
REQ_UP_ITEM 和 REQ_DOWN_ITEM
</p>
<p>
<dd>
这两个移动请求在上面的例子中已经出现过。这两个移动请求传递给menu_driver时,menu_driver从当前菜单项上移或下移一个菜单项。
</dd>
</p>
<p>
REQ_SCR_* 之类的移动请求
</p>
<p>
<dd>
REQ_SCR_ULINE, REQ_SCR_DLINE, REQ_SCR_DPAGE和 REQ_SCR_UPAGE是有关屏幕滚动的移动请求。如果所有的菜单项在当前菜单子窗口中显示不完,菜单就是可滚动的了。这些请求传给menu_driver时,分别向上/下滚动一行/页。
</dd>
</p>
<p>
REQ_FIRST_ITEM, REQ_LAST_ITEM, REQ_NEXT_ITEM 和REQ_PREV_ITEM
</p>
<p>
<dd>
这些移动请求请求的功能从名字上就可以明显的看出来。分别是移动到第一个菜单项、第二个菜单项、下一个菜单项和前一个菜单项。
</dd>
</p>
<p>
REQ_TOGGLE_ITEM
</p>
<p>
<dd>
当使用这个移动请求时,当前菜单项会被锁定。这个选项仅用于一个多层菜单。因此使用它时须关闭O_ONEVALUE。这个选项可以通过set_menu_opts()函数关闭或启用。
</dd>
</p>
<p>
样式匹配请求
</p>
<p>
<dd>
每个菜单都有一个与之关联的匹配缓冲区。通过这个缓冲区,用户可以通过输入ASCII字符样式查找与之匹配的菜单项。任何传给menu_driver的ASCII字符都会被送入样式匹配缓冲区,同时它试着从菜单项列表中查找最近的匹配项。然后立刻从当前菜单项跳转到那个与匹配样式最近的菜单项上去。REQ_CLEAR_PATTERN请求用来清空匹配缓冲区。REQ_BACK_PATTERN清除模式缓冲区的前一个匹配样式。如果有多个匹配样式,这些匹配样式就可以通过REQ_NEXT_PATTERN和REQ_PREV_PATTERN切换,这两个选项分别从当前菜单项移至下/上一个匹配样式。
</dd>
</p>
<p>
鼠标事件请求
</p>
<P>
<dd>
如果有鼠标事件请求,则根据鼠标的位置来产生相应的事件。在man帮助页是这样解释这些行为的:
</dd>
</P>
<i>
如果第二个参数是KEY_MOUSE的键值,相应的鼠标事件就会转化为上面已经定义的请求。现在你只须在用户窗口点击鼠标(如:在菜单显示区或窗口)。如果你是在菜单显示区单击的,将生成REQ_SCR_ULINE选项,如果是双击,就会生成REQ_SCR_UPAGE,如果单击三次,则生成REQ_FIRST_ITEM。如果你是在菜单显示区域的下方单击的,将生成REQ_SCR_DLINE,若是双击,就会生成REQ_SCR_DPAGE,若是单击三次的话,则生成REQ_LAST_ITEM。如果你单击菜单显示区内的某个菜单项,菜单的提示光标就定位在那个菜单项上了。
</i>
<p>
后面的程序中将会对以上的移动请求作适当的讲解。
</p>
<br>
<br>
<h3 align="left">
<a name="menuwindow">
17.4 含菜单窗口
</a>
</h3>
<br>
<p>
<dd>
每个已创建的菜单都对应着一个窗口和一个子窗口。菜单窗口显示菜单的标题或边框线。菜单子窗口显示当前可选的菜单项。但在上面的那个例子里我们并没有指定窗口或子窗口。当窗口未被指定时,stdscr将作为主窗口。然后菜单系统根据将要显示的菜单项规划计算子窗口的大小。这些菜单项就在规划好的子窗口中显示出来。接下来让我们就来通过玩转这些窗口:打印一个有边框线和标题的菜单。
</dd>
</p>
<p>
例19. 一个菜单窗口用法的例子
</p>
<font color="Maroon">
<pre>
#include &lt;menu.h&gt;
#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)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -