当前位置:Linux教程 - Linux - UNIX 萤幕导向程式的发展利器 - curses (二)

UNIX 萤幕导向程式的发展利器 - curses (二)

         UNIX 萤幕导向程式的发展利器 - curses (二)


                             校园网路策进会
                             会长  林建宏


   在上期为您介绍完了 curses.h 函式库的一些基本函式呼叫後在, 在本期里
  , 我们将继续为您介绍 curses 有关多视窗处理的函式. 有了这些函式, 我们
  可以在程式里同时处理多个不同的视窗. 如 joe 编辑器内我们可将萤幕切割
  成好几个小萤幕, 并且可以在这些不同的萤幕间做切换并编辑不同的档案, 这
  就是多视处理的应用. 另外, 有关 POP-UP 视窗的制作, 以及视窗的卷动, 在
  本文里, 我们将以简单的例子, 告诉您这些功能是如何做到的. 关於一些较基
  本函式的用法, 我们将不再特别介绍. 如果您尚未熟悉 curses 基本函式使用
  方法, 请参阅上一期 (80 期 ) 通讯.


 ■ 视窗的建立

  视窗的建立, 以 newwin() 这个函式来完成. 同时, 需宣告此视窗为 WINDOW
  结构变数.

  WINDOW *newwin(lines,colums,start_y,start_x);

   WINDOW *win;
   win=newwin(10,20,0,0);

  如此, 将以 (0,0) 为原点, 取一个 10 列 20 行的矩形为一新的视窗. 今後
  我们只要呼叫 win 这个变数, 就可以对这新视窗做处理.

   如: wmove(win,3,2);


 ■ 多视窗处理函式的格式

  这一类函式和一般的基本函式极为类似, 几乎每一个基本函式都有一个对应的
  视窗处理函式. 一般将 ''w'' 加在函式的里头作为区别, ''w'' 乃 ''window'' 之
  意. 另外, 因为可同时处理多个视窗, 在呼叫使用时, 需特别指定欲处理的视
  窗. 当然, 如果您指定对 stdscr 做处理, 由於是对标准输出入萤幕处理, 其
  作用将相当於一般基本的函式.

   如:

   wmove(win,y,x)   即对 win 这个视窗做 move() 动作.
   wmove(stdscr,y,x) 相当於 move(y,x)

  介绍一些较重要的函式

  wmove(win,y,x)
  touchwin(win)
  wrefresh(win)
  mvwaddstr(win,y,x,str)
  wattron(attr)
  delwin(win)
  subwin(win,ny,nx,y,x)

  其他函式多和基本函式互为对应, 故不全部列出, 详细名称可参考 curses
  的 online manual.

 ■ 视窗内的座标系

  视窗内的座标系, 将以此视窗的起始点为新原点, 并以其相对位置作为新的
  座标. 举例来说

  win=newwin(10,20,5,5);
  wmove(win,2,3);

  将以 (5,5) 为新原点, y 方向移动 2 单位, x 方向移动 3 单位. 因此实际
  上, 游标将移动至 y=7 x=8 的位置上.


 ■ POP-UP 视窗的建立

  利用 curses 所提供的视窗处理函式, 我们可以做出像 ONLINE HELP 的 POP
  -UP 画面. 当按下某键後, 一个新的视窗将像 "" 跳 "" 出来一般覆盖原来的画
  面. 当关掉此视窗後, 又不会影响到原来被覆盖的画面.


  下面的例子, 我们及模拟 ONLINE HELP 的形式, 当按下 ''h'' 键时, 视窗即出现


  #include

  main()
  {
   int ch,x,y;
   WINDOW *win;

   initscr();  ←┐
   cbreak;    │ 启动 curses 模式
   noecho();   │
   nonl();   ←┘

   win=newwin(4,30,LINES/2-3, COLS/2-15);/* 建立一个新视窗, 其中LINES,COLS *.
   box(win,''|'',''-'');           /* 为 curses 内定值, 即萤幕行/列数*.
   mvwaddstr(win,1,4,""This is another screen"");
   mvwaddstr(win,2,2,""Press anykey to continue.."");

   for (y=0;y   for (x=0;x    mvprintw(y,x,""@"");

   for(;;) {
    refresh();
    ch=getch();
    switch(ch) {
     case ''q'':        /* 按 ''q'' 键离开 */
          endwin();
          exit(0);

     case '' '':       /* 按 [TAB] 键 呼叫另一视窗  */
      touchwin(win);    /* wrefresh() 前需 touchwin() */
      wrefresh(win);
      getch();       /* 按任意键关闭视窗 */
      touchwin(stdscr);
      break;

     default:break;
    }
   }
  }


  执行结果:

   ┌────────────────────────────┐
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   └────────────────────────────┘
           ↑ 原来画面被 ''@'' 填满, 按下[TAB]键後
           ↓ 出现 POP-UP 画面.
   ┌────────────────────────────┐
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@+----------------------------+@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@|  This is another screen  |@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@| Press anykey to continue.. |@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@+----------------------------+@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   │ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ │
   └────────────────────────────┘


 ■ 视窗的卷动

  视窗的卷动, 掖Q用来配合视窗的处理, 当我们持续对视窗输出直到视窗的游
  标移动至最後一列时, 如果我们再输出一列或是输出一个换行字元时, 视窗可
  整个往上卷动一行. 这对我们撰写一个编辑程式时, 是尤其重要的, 一个画面
  无法卷动的编辑器, 势必无法处理超过一个萤幕大小的档案.

  视窗的卷动是预设为关闭的, 并以 scrollok() 来控制开闭.

  scrollok(win,TRUE);  开启
  scrollok(win,FALSE);  关闭


  下面的例子因为不断地输出 0,1,2.. 故将以一个 40 * 10 的视窗不停的卷动

   #include

   main()
    {
     int i;
     WINDOW *scrwin,*boxwin;

     initscr();   ←┐
     cbreak;     │ 启动 curses 模式
     noecho();    │
     nonl();    ←┘

     scrwin=newwin(10,40,LINES/2-6,COLS/2-25); /* 设定另一礸﹞j小 */
     boxwin=newwin(12,42,LINES/2-7,COLS/2-26); /* 设定外框视窗大小 */

     scrollok(scrwin,TRUE); /* 开启视窗卷动功能 */

     box(boxwin,''|'',''-'');
     refresh();
     wrefresh(boxwin);

     for (i=0;;++i)     /* 不断地在视窗内输出 0-8 的数字,使视窗卷动 */
      {
      wprintw(scrwin,""%d"",i%9);
      wrefresh(scrwin);
      }
    }


   执行结果:
      ┌──────────────────────┐
      │     +----------------------+      │
      │     |3456780123456780123412| ↑ 视   │
      │     |3456780123456780123456| │ 窗   │
      │     |7801234567801234567801| │ 不   │
      │     |2345678012345678012345| │ 停   │
      │     |6780123456780123456780| │ 往   │
      │     |1234567801234567801234| │ 上   │
      │     |5678012345678012345678| │ 卷   │
      │     |0123456780123456780123| │ 动   │
      │     +----------------------+      │
      │                      │
      └──────────────────────┘


 ■ 范例 - 模拟 joe 分割画面同时编辑两个档案

   在下面的例子里, 我们应用了多视窗处理的函式, 改良上回介绍的编辑器,
   在这个程式里, 我们可以同时编辑两个画面, 并以 [ESC] 做不同视窗间的
   切换. 同时, 按下 [TAB] 键, 会出现 POP-UP 的 ONLINE HELP.


  #include

  void initial();

  main()
  {
   WINDOW *win[2],*curwin,*helpwin;
   int nowwin;
   int x,y;
   int i;
   int ch;

   initial();

   win[0]=newwin(LINES/2-1,COLS-1,0,0);    /* 设定两个视窗的大小*/
   win[1]=newwin(LINES/2-1,COLS-1,LINES/2,0);

   helpwin=newwin(3,30,2,COLS/2-15 );    /* ONLINE HELP 的大小 */
   box(helpwin,''|'',''-'');
   mvwaddstr(helpwin,0,10,""ONLINE HELP"");  /* ONLINE HELP 的内容 */
   mvwaddstr(helpwin,1,4,""Hit any key to continue.."");

   for (i=0;i    mvaddch(LINES/2-1,i,''-'');

   nowwin=0;             /* 先指定游标在第一视窗 */
   curwin=win[nowwin];
   getyx(curwin,y,x);
   move(0,0);
   refresh();

   refresh();

   do {
    ch=getch();
    switch(ch) {

     case KEY_UP: --y;       /* 判断是否""↑""键被按下    */
           break;
     case KEY_DOWN: ++y;      /* 判断是否""↓""键被按下    */
           break;
     case KEY_RIGHT: ++x;     /* 判断是否""→""键被按下    */
           break;
     case KEY_LEFT: --x;      /* 判断是否""←""键被按下    */
           break;
     case '' '':          /* 判断是否 ENTER 键被按下  */
          ++y;
          x=0;
          break;
     case '' '':          /* 判断是否 TAB 键被按下   */
          touchwin(helpwin);
          wrefresh(helpwin); /* 呼叫 ONLINE HELP */
          getch();
          touchwin(win[1-nowwin]); /* 重画第一,二视窗 */
          wrefresh(win[1-nowwin]);
          touchwin(curwin);
          wrefresh(curwin);
          break;
     case 127:           /* 判断是否 BACKSPACE 键被按下 */
          wmove(curwin,y,--x);/* delete 一个字元      */
          waddch(curwin,'' '');
          break;

     case 27 : nowwin=1-nowwin;   /* [ESC] 键切换视窗 */
          curwin=win[nowwin];
          getyx(curwin,y,x);
          break;
     default:
          waddch(curwin,ch);
          x++;
          break;
    }
    wmove(curwin,y,x);
    wrefresh(curwin);
   } while(1);
  }

  void initial()
  {
   initscr();         ←┐
   cbreak();           │ 启动 curses 模式
   nonl();            │
   noecho();          ←┘
   intrflush(stdscr,FALSE);
   keypad(stdscr,TRUE);
   refresh();
  }


  执行结果:

      ┌─────────────────────────────┐
      │  screen1                        │
   ┌→ │     this is screen 1, you can press [ESC] to     │
 以 │  │   switch between screen 1 and screen 2.        │
[ESC]│  │                             │
 切 │  │                             │
 换 │  │----------------------------------------------------------│
 游 │  │   screen 2                       │
 标 │  │                             │
 位 └→ │    _ (游标)                     │
 置    │                             │
      └─────────────────────────────┘
                  ↑ 按下[TAB] 键,出现 ONLINE HELP
                  ↓
      ┌─────────────────────────────┐
      │  screen1                        │
      │     this is screen 1, you can press [ESC] to     │
      │   switch+---------ONLINE HELP--------+         │
      │      |  Hit any key to continue..|         │
      │      +----------------------------+         │
      │----------------------------------------------------------│
      │   screen 2                       │
      │                             │
      │                             │
      │                             │
      └─────────────────────────────┘
                  ↑ 按任意键, ONLINE HELP 关闭
                  ↓
      ┌─────────────────────────────┐
      │  screen1                        │
      │     this is screen 1, you can press [ESC] to     │
      │   switch between screen 1 and screen 2.        │
      │                             │
      │                             │
      │----------------------------------------------------------│
      │   screen 2                       │
      │                             │
      │    _ (游标)                     │
      │                             │
      └─────────────────────────────┘

■ 结语

  我们以连续两期来介绍 curses.h 函式库的使用方法, 相信同学对撰写这类的
  程式应该不再陌生. 所谓『戏法人人会变, 巧妙各有不同』. 知道了基本函式
  的呼叫方法, 能不能写出实用的程式, 就靠各位的巧思和创造力了.

  有任何问题建议, 欢迎 E-mail 至 [email protected] , 谢谢 !