免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4076 | 回复: 2
打印 上一主题 下一主题

[SCO UNIX] 在Sco下利用Curses/Form建立简单的用户界面(二) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-01-04 23:21 |只看该作者 |倒序浏览

  1. 在Sco下利用Curses/Form建立简单的用户界面
  2. 当我们建立了workwin,headwin,msgwin之后,在headwin上显示了一些抬头信息,在msgwin中显示了提示信息,而workwin留着为用户提供一个真正的输入/输出界面。
  3. 有时,我们希望在屏幕上看到象一些银行系统中使用的画面一样,一些项目、后面跟着要求你输入或随时依条件显示的内容,这些都是用户提供的,不可能在编程时确定。
  4. 利用curses例程主要是wgetch()可以编写出接受用户输入的程序,可以实现在屏幕上提供一块接收用户输入数据区域的功能,有时我们将这个区域叫域(field),而将含有一些域的屏幕叫表单(form)。利用wgetch()等例程可以实现表单和域,也可以使域只接收固定类型的值(比如只接收数值)、自动换行等,但要这么实现,你得做大量工作,并且要考虑周全,除非你只想实现一个简单的演示界面。
  5. 当然,界面的简单与否在于用户的看法,应该使之具有一定提示性,让用户明白他们应该在哪里输入,输入什么,以及具有一定的方便性,输入方式的一致性,以使用户使用便捷,比如各form的功能键定义应一致。
  6. curses库中提供了一组有关form和field的例程,他们组成了libform.a库,他给程序员提供了一个操作form和field的高级接口,以使程序员不必自己考虑如何建立form和field,但是即使这样,用form库编程,也是一件麻烦事。
  7. 1、form介绍
  8. 一个表单(form)是包含很多域(field)的集合,每个域可以是提示(标签)也可以是数据输入项。
  9. 通常通过以下几个步骤控制一个form程序:
  10. 。初始化curses。
  11. 。使用new_field()建立form所用的域。
  12. 。建立form并张贴它。
  13. 。刷新屏幕。
  14. 。建立一个循环,处理用户请求。
  15. 。当得到用户退出请求或,unpost表单。
  16. 。释放form所占内存。
  17. 。释放fields所占内存。
  18. 。终止curses。
  19. 表单中的域可以有多行,这里只考虑单行域。
  20. 2、建立form
  21. 要建立form首先要建立他所包含的域,建立域的函数是如下格式:
  22. FIELD *new_field(height, width, top, left, offscreen, nbuf)
  23. int        height, width;
  24. int        top, left;
  25. int        offscreen;
  26. int        nbuf;
  27. 头两个参数为域的高度和宽度,top和left指明域的左上角坐标,offscreen通常为0。
  28. 最重要的参数是nbuf,他指明你希望为这个域分配的缓冲区的个数,form库为每个field分配一个工作缓冲区,大小是:
  29.         (height + offscreen) * width + 1
  30. 域中的每个字符位置对应缓冲区的一个字符位置,最后缓冲区内的结尾加上一个null字符以标识结束,nbuf就是指明另外使用几个附加的这样的缓冲区,一般值在0--7之间,你可以使用附加的缓冲区存储你有用的数据,比如密码,因为0号缓冲区的内容默认是显示在屏幕上的。
  31. 创建表单的函数
  32. FORM *new_form(fields)
  33. FIELD        **fields;
  34. 这个函数的作用是将前面建立的域与表单关联。
  35. 参数是一个FIELD指针数组,注意:最后一个应该是(FIELD *)NULL,这个指针数组中field的次序决定表单中域的访问次序。
  36. 域的属性
  37. 除了域缓冲区之外,每个域还有其他属性:
  38. (1)、域选项
  39. 这是个位选择掩码(模),有以下几种定义:
  40. O_ACTIVE O_VISIBLE O_PUBLIC O_WRAP O_BLANK O_AUTOSKIP O_PASSOK O_STATIC
  41. 默认时他们都是可用的,最有用的是O_ACTIVE、O_VISIBLE、O_AUTOSKIP。比如要建立一个标签域,它是不可以活动的,即不可以接收数据,光标也不在其上停留。比如下面的建立标签片段:
  42. f[2] = new_field(1, 10, 4, 18, 0, 0);
  43. field_opts_off(f[2], O_ACTIVE);
  44. (2)、域对齐方式
  45. int set_field_just(field, justification)
  46. FIELD        *field;
  47. int        justification;
  48. 对齐方式有:NO_JUSTICATION JUSTIFY_LEFT JUSTFY_RIGHT JUSTFY_CENTER
  49. (3)、域的显示属性
  50. 对于单个域,可以设置前景、背景,前景对应与输入的数据,背景对应与整个域。这里的显示属性就是由attrset()设置的curses的显示属性,类型为chtype。
  51. (4)、域数据类型
  52. TYPE_ALPHA —— 可接收[a-zA-z]
  53. set_field_type(field, TYPE_ALPHA, width)
  54. int        width;
  55. width为可接收的长度。

  56. TYPE_ALNUM —— 可接收数字和字母,不包括特殊字符。
  57. set_field_type(field, TYPE_ALNUM, width)

  58. TYPE_ENUM —— 可接收给定集合中包含的数据。
  59. set_field_type(field, TYPE_ENUM, keyword_list, checkcase, checkunique)
  60. char        **keyword_list;      /* 以NULL结尾的字符串数组 */
  61. int        checkcase;           /* 是否区分大小写        */
  62. int        checkunique;         /* 自动完成匹配工作      */

  63. TYPE_INTEGER  —— 可以接收数据限定为整数和'-'号。
  64. set_field_type(field, TYPE_INTEGER, precision, vmin, vmax)
  65. int        precision;
  66. long        vmix, vmax;
  67. precision : 精度;
  68. vmin vmax :范围。
  69. 此精度表示右对齐或应有的数据宽度,如果为4,输入‘18’后将显示‘0018’。

  70. TYPE_NUMERIC —— 十进制数。
  71. set_field_type(field, TYPE_NUMREC, precision, vmin, vmax)
  72. 这里的精度是指小数位,与前面的整数精度意义不同。

  73. TYPE_REGEXP —— 接受正则表达式。
  74. set_field_type(field, TYPE_REGEXP, expression)
  75. char        *expression;
  76. expression : 表示正则式的字符串。
  77. 用这个类型可以做出接受行如"xxx.xxx.xxx.xxx"IP地址的域,还有电话号码域、日期时间域等,可惜它不支持扩展的正则表达式。

  78. 3、张贴表单
  79. 前面我们建立了一些域f[n],并将之与表单关联了,但是现在还不能在屏幕上看到这些,表单必须张贴才能看到。
  80. 下面是一个显示表单的函数,它调用一个复杂的建立表单及其关联域的函数cl_createform(),以后再讲它,然后用域钩函数来设定当前域的显示属性,设置表单窗口和表单子窗口,最后张贴表单,刷新窗口。
  81. 注意:每个表单都有一个表单窗口和一个子窗口,一般来讲,你要在哪个窗口显示表单,就将表单窗口和子窗口设成那个窗口。

  82. chtype        inp_attr;

  83. FORM *cl_dispform(win, hm, fhm, attr)
  84. WINDOW        *win;
  85. char        *hm, *fhm;
  86. chtype        attr;
  87. {

  88.         static FORM        *form;

  89.         inp_attr = attr;
  90.         form = cl_createform(hm, fhm);

  91.         if(!form)        return((FORM *)0);
  92.        
  93.         set_field_init(form, cl_onbold);
  94.         set_field_term(form, cl_offbold);

  95.         set_form_win(form, win);
  96.         set_form_sub(form, win);

  97.         post_form(form);

  98.         cl_drawline(win, 0, 0, 0, 80);
  99.         cl_drawline(win, FORM_ELINE, 0, 0, 80);
  100.         pos_form_cursor(form);

  101.         wrefresh(win);

  102.         return(form);

  103. }

  104. void cl_offbold(form)
  105. FORM        *form;
  106. {
  107.         set_field_back(current_field(form), A_NORMAL);
  108. }

  109. void cl_onbold(form)
  110. FORM        *form;
  111. {
  112.         set_field_back(current_field(form), inp_attr);
  113. }

  114. 需要解释的是表单光标定位函数pos_form_cursor()。一些用户可能在表单处理过程中将表单的光标从特定位置移开,此函数就是为连续处理用户请求而设计,它使表单光标重新归位。

  115. 4、建立循环以处理用户请求
  116. 表单开发和核心部分就是建立一个驱动处理用户的请求,以达到与用户交互的目的。
  117. 表单驱动响应如下请求:
  118. 。页面浏览请求
  119.         REQ_NEXT_PAGE        REQ_PREV_PAGE
  120.         REQ_LAST_PAGE        REQ_FIRST_PAGE
  121.         主要用在多页表单浏览的需要。
  122. 。域间浏览请求
  123.         REQ_NEXT_FIELD        REQ_UP_FIELD
  124.         ......
  125.         共12个
  126.         这些请求是指明在表单中各个域之间的光标移动,域间浏览时光标的移动有许多方式。
  127. 。域内浏览请求
  128.         REQ_PREV_CHAR        REQ_END_FIELD
  129.         ......
  130.         共14个
  131.         这些请求是在一个域的内部的光标移动形式。
  132. 。滚动请求
  133.         REQ_SCR_FLINE等
  134.         主要用在多行域。
  135. 。域内编辑请求
  136.         域内编辑请求指示在一个域内对域中的内容的增加、删除的编辑的功能,它有两种模式:REQ_INS_MODE和REQ_OVL_MODE,完整的域内编辑请求是
  137.         REQ_DEL_CHAR        REQ_DEL_PREV
  138.         REQ_CLR_EOL        REQ_CLR_EOF
  139.         REC_CLR_FIELD
  140.         其中主要用到的是REQ_DEL_CHAR,REQ_CLR_FIELD。
  141. 。域校验请求
  142.         REQ_VALIDATION
  143.         这是最重要的一个请求,用来对域中的数据类型和格式进行校验,有时在我们不离开域的时候就想校验的情况下,这个请求很有用,它也负责刷新数据缓冲区。这里所说的不离开/离开是指光标仍在本域之内,或者表单就一个活动域的情况下,域填满且光标没有回到域的开头。

  144. 下面我们来研究以下,怎样做出一个表单驱动来处理用户请求:
  145. 首先,我们需要做一个用户域校验函数:cl_virtualize(),它的作用是等待用户从域窗口输入一个字符,这里有一个结构数组,它的作用是使字符和我们要用的请求对应。此函数接收到字符后查找这个结构数组,如果字符数组中找到这个字符,则返回它对应的请求,否则,直接返回字符。

  146. int cl_virtualize(f, win)
  147. FORM        *f;
  148. WINDOW        *win;
  149. {
  150.         int        mode = REQ_OVL_MODE;
  151.         unsigned        n;
  152.         int        c;
  153.         FIELD        *me;
  154.         struct        {
  155.                 int        code;
  156.                 int        result;
  157.         }        lookup[] = {
  158.         {        CTRL('A'), REQ_NEXT_CHOICE        },
  159.         {        CTRL('C'), REQ_CLR_FIELD        },
  160.         {        CTRL('S'), REQ_RIGHT_FIELD        },
  161.         {        CTRL('D'), REQ_DOWN_FIELD        },
  162.         {        CTRL('F'), REQ_LEFT_FIELD        },
  163.         {        CTRL('E'), REQ_UP_FIELD        },
  164.         {        CTRL('X'), REQ_DEL_CHAR        },
  165.         {        CTRL('H'), REQ_DEL_PREV        },
  166.         {        KEY_CR,        REQ_NEXT_FIELD },
  167.         {        KEY_DOWN, REQ_NEXT_FIELD        },
  168.         {        KEY_UP, REQ_PREV_FIELD        },
  169.         {        KEY_LEFT, REQ_LEFT_CHAR        },
  170.         {        KEY_RIGHT, REQ_RIGHT_CHAR        },
  171.         {        KEY_BACKSPACE, REQ_DEL_PREV        },
  172.         {        KEY_HOME, REQ_FIRST_FIELD        },
  173.         {        KEY_END, REQ_LAST_FIELD        },
  174.         {        KEY_NPAGE, REQ_NEXT_PAGE        },
  175.         {        KEY_PPAGE, REQ_PREV_FIELD        },
  176.         {        KEY_F(1), MAX_FORM_COMMAND + 1        },
  177.         {        KEY_F(2), MAX_FORM_COMMAND + 2        },
  178.         {        KEY_F(3), MAX_FORM_COMMAND + 3        },
  179.         {        KEY_F(4), MAX_FORM_COMMAND + 4        },
  180.         {        KEY_F(5), MAX_FORM_COMMAND + 5        },
  181.         {        KEY_F(6), MAX_FORM_COMMAND + 6        },
  182.         {        KEY_F(7), MAX_FORM_COMMAND + 7        },
  183.         {        KEY_F(8), MAX_FORM_COMMAND + 8        },
  184.         {        KEY_QUIT, MAX_FORM_COMMAND + 9        },
  185.         {        KEY_ESCAPE, MAX_FORM_COMMAND + 9        },
  186.         {        KEY_AUTH, MAX_FORM_COMMAND + 2        },
  187.         {        KEY_COMMIT, MAX_FORM_COMMAND + 4        }
  188.         };

  189.         c = wgetch(win);
  190.         me = current_field(f);

  191.         for(n = 0; n < sizeof(lookup) / sizeof(lookup[0]); n++)        {
  192.                 if(lookup[n].code == c)        {
  193.                         c = lookup[n].result;
  194.                         break;
  195.                 }
  196.         }
  197.        
  198.         if(c <= KEY_MAX)        {
  199.                 c = cl_editpassword(me, c);
  200.         } else if(c <= MAX_FORM_COMMAND)        {
  201.                 c = cl_editpassword(me, c);
  202.         }
  203.         return(c);
  204. }

  205. 这个函数的主要作用是将输入的字符与请求值对应。
  206. 注意:cl_editpassword()为密码域处理函数,取自linux-HOWTO-curses的示例程序的form部分。
  207. 第二部就是再定义一个函数将请求值数值化,看起来多此一举,确实如此,但这样使程序结构更清晰。


  208. int cl_getrequest(c)
  209. int        c;
  210. {

  211.         int        rc = 0;
  212.        
  213.         switch(c)        {
  214.         case        MAX_FORM_COMMAND + 1 :
  215.                 rc = 1;
  216.                 break;
  217.         case        MAX_FORM_COMMAND + 2 :
  218.                 rc = 2;
  219.                 break;
  220.         case        MAX_FORM_COMMAND + 3 :
  221.                 rc = 3;
  222.                 break;
  223.         case        MAX_FORM_COMMAND + 4 :
  224.                 rc = 4;
  225.                 break;
  226.         case        MAX_FORM_COMMAND + 5 :
  227.                 rc = 5;
  228.                 break;
  229.         case        MAX_FORM_COMMAND + 6 :
  230.                 rc = 6;
  231.                 break;
  232.         case        MAX_FORM_COMMAND + 7 :
  233.                 rc = 7;
  234.                 break;
  235.         case        MAX_FORM_COMMAND + 8 :
  236.                 rc = 8;
  237.                 break;
  238.         case        MAX_FORM_COMMAND + 9 :
  239.                 rc = 9;
  240.                 break;
  241.         default :
  242.                 rc = 0;
  243.                 beep();
  244.         }
  245.         return(rc);
  246. }
  247. 此函数就是将用户自定义的请求转换成数值,以便在程序中唯一确定一个请求。

  248. 接下来就要处理用户表单输入了:
  249. 假使有一个输入函数,它在参数给出的form中输入数据,同时响应用户各种各样的请求,那么我们可以以以下结构编写这个输入处理函数:

  250. int cl_inputdata(win, form, imask, idata)
  251. WINDOW        *win;
  252. FORM        *form;
  253. unsigned long        imask;
  254. char        *idata;
  255. {
  256.        
  257.         int        rc;
  258.         int        finish = 0;
  259.         int        fldidx;
  260.         int        fldcnt;
  261.         int        rows, cols, frow, fcol, nrow, nbuf;

  262.         FIELD        **flist = (FIELD **)0;

  263.         fldcnt = field_count(form);
  264.         cl_setcurrent(win);

  265.         cl_outputdata(win, form, imask, idata, 1);
  266.         memset(idata, 0, DATA_DEFAU_LEN);

  267.         while(!finish)        {
  268.                 switch(form_driver(form, rc = cl_virtualize(form, workwin)))
  269.                 {
  270.                 case        E_OK:
  271.                                 break;
  272.                 case        E_UNKNOWN_COMMAND :
  273.                                 switch(cl_getrequest(rc))        {
  274.                                 case        9:
  275.                                         finish = 9;
  276.                                         break;
  277.                                 case        4:
  278.                                         finish = 4;
  279.                                         form_driver(form, REQ_VALIDATION);
  280.                                         flist = form_fields(form);
  281. for(fldidx = 0; fldidx < fldcnt; fldidx++)        {
  282.         if(ut_getbits(imask, fldidx))        {
  283.                 if(field_info(flist[fldidx], &rows, &cols,
  284.                 &frow, &fcol, &nrow, &nbuf) == E_OK && nbuf > 0)        {
  285.                         strcat(idata, field_buffer(flist[fldidx], 1));
  286.                         strcat(idata, "|");
  287.                 } else {
  288.                         strcat(idata, field_buffer(flist[fldidx], 0));
  289.                         strcat(idata, "|");
  290.                 }
  291.         }
  292. }
  293.                                         break;
  294.                                 default :
  295.                                         finish = 0;
  296.                                         break;
  297.                                 }        /* switch */
  298.                         break;
  299.                 default :
  300. /*
  301. ------------------------if using this then form input no loop ---------------
  302.                         finish = -1;
  303. */
  304.                         break;
  305.                 }        /* switch */
  306.         }                /* while */
  307.         return(rc);
  308. }

  309. 此函数里面的idata是存放全部域数据的一块缓冲区,imask是一个掩码,指明那些域的数据有用,需要返回,cl_outputdata()是输出idata中原始数据的,这里可以不用管它。这里最主要的函数是form_driver(),这是表单驱动主函数,我们建立个循环,让它在循环中一直接收用户的输入,除非用户输入[F4] [ESC]键,form_dirver()接收从cl_virtualize()返回的字符,进行比较、校验,他的返回值有两个:E_OK,E_UNKNOWN_COMMAND,E_OK表示它接受的按键在KEY_MAX和MAX_COMMAND之间,而E_UNKNOWN_COMMAND则表示它不认识cl_virtualize()传回的键值,在这种情况下我们再用cl_getrequest()固定以下这个键值的数值代码,虽然只有4,9两个值但我们还使用了switch,便于以后扩充。在这里,当我们按下[F4]时,调用请求form_dirver(form, REQ_VALIDATION)使未结束的域输入也有效地进行检查,写到0号缓冲区内,然后置finish = 4, 结束循环;当按下[ESC]键时,直接结束循环。

  310. 5、我们按下[F4]键接收了数据或按下[ESC]键放弃数据之后,如果不再使用该表单及其关联域,我们就可以释放它们:
  311. void cl_clearform(form)
  312. FORM        *form;
  313. {
  314.        
  315.         WINDOW        *w;
  316.         FIELD        **f = (FIELD **)0;

  317.         if(!form)        return;

  318.         w = form_win(form);
  319.         f = form_fields(form);

  320.         unpost_form(form);
  321.         free_form(form);
  322.         wrefresh(w);
  323.        
  324.         while(*f)       
  325.                 free_field(*f++);

  326.         return;
  327. }
  328. 此函数首先unpost_form,再释放form空间,然后找到其关联域,一个个释放它们。

  329. 6、如果此时退出curses,则可用前面提到的cl_endwindow()处理。

  330. (未完待续)
复制代码

论坛徽章:
0
2 [报告]
发表于 2007-01-05 09:05 |只看该作者
坐着板凳听了,呵呵

论坛徽章:
0
3 [报告]
发表于 2007-11-13 11:57 |只看该作者

102531

支持楼主,请各位unix爱好者加群号102531,强烈欢迎ChinaUnix各位楼主、精华区牛人的加入!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP