当前位置: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); /* 设定另一视窗大小 */
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] , 谢谢 !


发信人: [email protected] (Cardinal), 信区: unix
标 题: Re: 请问谁会用 curses 显示 ANSI color 字
发信站: 台大电机 Maxwell 站

首先声明,这一封的内容应该属於 programming board,但是现在有不止一个
人问我这个问题,所以在这个版再把详细的方法说明一遍。如果有人看不懂而
仍然有兴趣的,请 mail 给我 ([email protected]) ,不要在
这边 reply,我会考虑在私下或在 programming board解决你的问题。

==> 在 Cardinal@Maxwell (Cardinal) 的文章中提到:
: 1.开一个 new window (newwin)
: 2.设定 window 的彩色属性 (wattrset)

==> 在设定彩色属性之前应该先设定颜色的 ""pair"" ,所谓的 ""pair"" 是指
foreground及background的颜色。curses的颜色有下面几种 (type为
short) :
COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE,
COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE

用init_pair(short pair, short f_color, short b_color)来设定 pair,
for example:
init_pair(100, COLOR_RED, COLOR_BLUE)
就设定了编号为 100, 蓝底红字的 color pair 了.

另外你要是嫌这几种颜色太单调了, 可以用 init_color 来设定色彩, 细节
这边就不谈了.

然後就用 wattrset(WINDOW* pwindow, short color_pair)设定你window的
颜色, for example:
wattrset(pwindow, 100) 就设定了一个蓝底红字的 window (不要忘记
这儿的 100 是刚刚用 init_pair设定的值)

: 3.印在 window 的字就自动变成那个颜色了 (mvwprintw, mvwaddstr, ...)

==> 这句... 该不会有问题吧.

: 4.想要印不同颜色的字,只要把那个字 ""OR"" (|) 不同的颜色即可 (记住,
: 这种有属性的字要用 int,不能用 char)

==> 其实型别不是用 int, 而是用 chtype (不过没有差别, 去查查 curses.h就
知道) , 譬如说, 你想要在刚刚设定为蓝底红字的 window印一个别的颜色的
''A'' 字, 可以这麽做 :

init_pair(another_color_pair, COLOR_随便, COLOR_随便) -->先设定另一
个 color pair
char cascii = ''A'';
chtype cascii_color = cascii | another_color_pair;

再把 cascii_color 印出来就是一个你想要颜色的 A 了.

--
~ Cardinal ~

From: Cardinal (Cardinal)
Title: 关於精华区...
Date: Fri Mar 10 20:36:27 1995


您好:

在 programming 版精华区 unix - curses libraries 中有一篇文章是我写的,
刚刚来这边找资料时翻到的, 真是受宠若惊. 不过原来的文章 (如何用 curses
显示彩色) 有一点忘了提到, 希望您能把下面的说明加进去:

1. init_color及init_pair 是 SystemVR3以後的标准, 不适用於 BSD 或
SunOS.
2. 在 SunOS 上要达成这样的目的, 我知道的有两种解法
a.有一款大同的中文工作站有支援 init_color & init_pair 的 library
这一型的 library 与 SunOS 为 object-code compatible.
b.ncurses 支援 init_pair & init_color