窗口直到现在,我们一直将终端作为一个全屏幕的媒介来使用。通常对于小而简单的程序来说这已经足够了,但是curses库所走的路还要更远一些。我们可以同时在物理屏幕上显示多个不同的尺寸的窗口。在这一节将要讨论到一些函数只会被X/Open扩展的curses所支持。然而,因为他们被ncurses所支持,所以在大多数的平台上使用并没有太多的问题。我们现在就来讨论使用多个窗口的问题。我们同时也会看到到目前为止我们所使用的这些命令如何生成多个窗口场景。
WINDOW结构尽管我们已经提到了stdscr,标准屏幕,但是我们现在并不需要使用他,因为到目前为止我们所讨论的几乎所有的函数都假定他们在stdscr上起作用,而且他们并不需要作为一个参数来传递。
stdscr只是WINDOW结构的一个特殊情况,就如同stdout是文件流的一个特殊情况一样。WINDOW结构通常是在curses.h中定义的,然而试验这个结构是有益的,但是程序绝不应直接访问他,因为这个结构可以而确定在不同的实现之间发生变化。
我们可以使用newewin与delwin调用来创建和销毁窗口:
#include <curses.h>
WINDOW *newwin(int num_of_lines, int num_of_cols, int start_y, int start_x);
int delwin(WINDOW *window_to_delete);
newwin函数通过屏幕位置(start_y,start_x)以及指定的行数和列数来创建一个新窗口。他返回一个指向新窗口的指针,如果创建失败则会返回null。如果我们希望将新窗口的右下角位于屏幕的右下角,我们可以将行数或是列数指定为零。所有的窗口必须适合当前屏幕,所以如果新窗口任何部分超出了屏幕区域,newwin函数就会失败。由newwin所创建的新窗口完全独立于已存在的窗口。默认情况下,他将会新生成的窗口放置在任何已存在的窗口之上,并且隐藏(不是改变)其内容。
delwin函数删除一个由newwin所生成的窗口。因为也许在调用newwin时已经分配了内存,所以我们应总是在不再需要窗口时删除这些窗口。要小心,绝不要试图删除curses自己的窗口,stdscr与curscr。
创建了一个新窗口之后,如何向其输出内容呢?答案是到目前我们所看到的这些函数都有通用的版本可以在特殊的窗口上进行操作,而且为了方便,同时也包括光标动作。
通用函数我们已经使用addch和printw函数向屏幕添加字符。与许多其他函数一样,他们可以使用前缀进行扩展,窗口为的前缀为w,移动的前缀为mv,移动与窗口的为mvw。如果我们查看curscr头文件,我们就会发现到目前为止我们所看到的这些函数只是简单的调用这些通用函数的宏。
当添加w前缀时,一个额外的WINDOW指针必须添加到参数列表中。当添加mv前缀时,两个额外的参数,y与x位置,必须添加到参数列表中。他们指明了执行操作的位置。y与x是相对于窗口的,而不是屏幕,(0,0)是窗口的左上角。
当添加mvw前缀进,三个额外的参数,一个WINDOW指针与y和x的值必须添加到参数列表中。令人不解的是,WINDOW指针必须总是位于屏幕坐标的前面,尽管有时前缀指示y与x在前面。
作为一个例子,下面是一个完整的addch与printw函数的原型集合。
int addch(const chtype char);
int waddch(WINDOW *window_pointer, const chtype char)
int mvaddch(int y, int x, const chtype char);
int mvwaddch(WINDOW *window_pointer, int y, int x, const chtype char);
int printw(char *format, ...);
int wprintw(WINDOW *window_pointer, char *format, ...);
int mvprintw(int y, int x, char *format, ...);
int mvwprintw(WINDOW *window_pointer, int y, int x, char *format, ...);
许多其他的函数,例如inch,同具有move与window变体可用。
移动与更新一个窗口下面的这些命令允许我们移动与重绘窗口。
#include <curses.h>
int mvwin(WINDOW *window_to_move, int new_y, int new_x);
int wrefresh(WINDOW *window_ptr);
int wclear(WINDOW *window_ptr);
int werase(WINDOW *window_ptr);
int touchwin(WINDOW *window_ptr);
int scrollok(WINDOW *window_ptr, bool scroll_flag);
int scroll(WINDOW *window_ptr);
mvwin函数在屏幕上移动一个窗口。因为一个窗口的所有部分都要适合屏幕区域,所以如果我们尝试将一个窗口的任何部分移出屏幕区域之外,mvwin函数就会失败。
wrefresh,wclear,werase函数只是我们前面见到的函数的通用形式;他们需要一个WINDOW指针,从而可以指向一个特定的窗口,而不是stdscr。
touchwin函数是比较特殊的。他会通知curses库其参数所指向的窗口内容已经发生了改变。这就意味着curses库会在下一次调用wrefresh时重新绘制窗口,尽管我们并没有实际的改变窗口内容。当我们有多个窗口在屏幕上层叠显示而我们需要决定显示哪个窗口时,这个函数会非常有用。
两个滚动函数控制一个窗口的滚动。scrollok函数,当传递一个布尔值true(通常为非零)时会允许一个窗口的滚动。默认情况下,窗口并不可以滚动。scroll函数只是简单的将窗口向上滚动一行。一些curses实现也会有一个wsctl函数,这个函数带有一个要滚动的行数,通常是一个负数。我们会在后面回到滚动的话题。
试验--多窗口现在我们已经了解了如何来管理多个窗口,现在我们将这些新函数放在一个程序multiwl.c中。为了描述的简洁,我们在这里省去了错误检测。
1 如平常一样,首先我们由定义开始:
#include <unistd.h>
#include <stdlib.h>
#include <curses.h>
int main()
{
WINDOW *new_window_ptr;
WINDOW *popup_window_ptr;
int x_loop;
int y_loop;
char a_letter = ‘a’;
initscr();
2 然后我们使用字符来填充基本窗口,一旦逻辑屏幕填充完毕刷新实际的屏幕。
move(5, 5);
printw(“%s”, “Testing multiple windows”);
refresh();
for (y_loop = 0; y_loop < LINES - 1; y_loop++) {
for (x_loop = 0; x_loop < COLS - 1; x_loop++) {
mvwaddch(stdscr, y_loop, x_loop, a_letter);
a_letter++;
if (a_letter > ‘z’) a_letter = ‘a’;
}
}
/* Update the screen */
refresh();
sleep(2);
3 现在我们创建一个新的10x20的窗口,并且在屏幕上绘制出窗口之前向其中添加一些文本。
new_window_ptr = newwin(10, 20, 5, 5);
mvwprintw(new_window_ptr, 2, 2, “%s”, “Hello World”);
mvwprintw(new_window_ptr, 5, 2, “%s”,
“Notice how very long lines wrap inside the window”);
wrefresh(new_window_ptr);
sleep(2);
4 现在我们改变背景窗体的内容,当我们刷新屏幕时, 由new_window_ptr所指向的窗体会变得模糊。
a_letter = ‘0’;
for (y_loop = 0; y_loop < LINES -1; y_loop++) {
for (x_loop = 0; x_loop < COLS - 1; x_loop++) {
mvwaddch(stdscr, y_loop, x_loop, a_letter);
a_letter++;
if (a_letter > ‘9’)
a_letter = ‘0’;
}
}
refresh();
sleep(2);
5 如果进行函数调用刷新新窗体,那么不会改动任何内容,因为我们还没有改动新窗体。
wrefresh(new_window_ptr);
sleep(2);
6 但是如果我们首先触发新窗体,并且使得curses认为窗体已经发生了改变,接下来调用wrefresh则会将新窗体再一次出现在前端。
touchwin(new_window_ptr);
wrefresh(new_window_ptr);
sleep(2);
7 接下来,我们再添加另外一个有盒子包围的重叠窗体。
popup_window_ptr = newwin(10, 20, 8, 8);
box(popup_window_ptr, ‘|’, ‘-’);
mvwprintw(popup_window_ptr, 5, 2, “%s”, “Pop Up Window!”);
wrefresh(popup_window_ptr);
sleep(2);
8 然后我们在清除并删除他们之前使用新的弹出窗体。
touchwin(new_window_ptr);
wrefresh(new_window_ptr);
sleep(2);
wclear(new_window_ptr);
wrefresh(new_window_ptr);
sleep(2);
delwin(new_window_ptr);
touchwin(popup_window_ptr);
wrefresh(popup_window_ptr);
sleep(2);
delwin(popup_window_ptr);
touchwin(stdscr);
refresh();
sleep(2);
endwin();
exit(EXIT_SUCCESS);
}
不幸的是,在本书中显示运行效果是不实际的。
正如我们由这段示例代码中所看到的,我们需要小心的刷新窗体来保证他们以正确的顺序出现在屏幕上。如果我们要求curses刷新一个窗体,他并不会存储关于窗体层次的任何信息。为了保证curses以正确的顺序绘制窗体,我们必须以正确的顺序对他们进行刷新。其中一个方法就是将我们窗体的所有指针存储在一个数组或是列表中,而这个数组或是列表是以他们应出现在屏幕上的顺序而维护的。
优化屏幕刷新正如我们在前面所看到的,刷新多个窗体需要一些繁琐,但并不是完全的繁重。然而,当要更新的终端是在一个慢速的链接上时就会出现一个更为严重的问题。幸运的时,现在这已不再是一个严重的问题,但是为了完整性我们将会演示这个问题的处理是多么的容易。
目标就是要尽量减少要在屏幕上的绘制的字符数,因为在一个慢速的链接上,屏幕的绘制相当的慢。curses使用两个特殊的函数来进行处理,wnoutrefresh与doupdate。
#include <curses.h>
int wnoutrefresh(WINDOW *window_ptr);
int doupdate(void);
wnoutrefresh函数决定哪些字符需要发送到屏幕,但是并不实际的发送。doupdate函数向终端发送实际的改变。如果我们只是简单的调用wnoutrefresh,其后立即调用doupdate,其效果就如同调用wrefresh一样。
然而,如果我们希望重新绘制一个窗体栈,我们可以在每一个窗体(当然需要以正确的顺序)来调用wnoutrefresh函数,然而在最后一个wnoutrefresh函数之后调用doupdate函数。这个使得curses按顺序在每一个窗体上执行屏幕更新计算,并且只输出更新的屏幕。这会使得curses尽量减少需要发送的字符数。
分享到:
相关推荐
### 使用curses管理基于文本的屏幕 #### 一、curses简介 curses是一个用于创建基于文本的用户界面的库,它在Linux和类UNIX环境中非常流行。curses的重要性在于它提供了一个介于简单的基于行的程序和复杂的图形界面...
即使是编写基于字符的全屏幕程序,使用curses函数库的方案也更简明,而程序本身也更独立于具体的终端。在编写这类程序时,使用curses函数库更比直接使用escape转义序列容易得多。curses函数库还可以对键盘进行管理,...
- 它提供了高级的文本输出和输入功能,比如分割屏幕、创建窗口、接受用户输入等,适合创建基于命令行的图形化界面。 - 在Windows系统上,curses库通常通过pdcurses或win32curses这样的移植版本来实现。 2. **whl...
**Python Curses**库是Python对原始C语言的`curses`库的封装,它提供了一套API,使得在基于文本的终端上创建图形用户界面成为可能。`curses`库可以处理键盘输入、屏幕输出和窗口管理,常用于控制台应用,如命令行...
curses库是用于处理文本模式用户界面的工具,特别是在没有图形窗口系统的情况下,它能够方便地在终端上进行屏幕管理和输入处理。通过使用curses,开发者可以更容易地控制屏幕输出和键盘输入,使得在命令行环境下也能...
在Python编程领域,curses库是一个至关重要的工具,尤其在开发基于终端的用户界面时。curses库源于C语言,但在Python中被广泛使用,因为它允许开发者创建全屏交互式的应用程序,使得在命令行界面下的用户体验得以...
通过对这些文件的分析和学习,我们可以更深入地理解如何使用`curses`库来开发一个基于文本的多人游戏。这个项目可能包括了游戏规则、角色移动、碰撞检测、用户输入处理以及网络通信等方面的具体实现,这些都是游戏...
在实际应用中,Python的curses库提供了许多函数和类,如`initscr()`用于初始化屏幕,`getch()`用于接收用户输入,`printw()`用于在屏幕上打印文本,以及`color_pair()`和`attron()`等用于设置颜色和文本属性。...
Python的curses库是用于创建基于终端的交互式应用程序的模块,它允许程序员在文本模式下处理用户输入和显示复杂的屏幕布局。`python_curses-2.2.2-cp39-cp39-win32.whl` 是一个专门为Python 3.9版本编译的curses库的...
它能够管理屏幕上的窗口,处理键盘输入,以及更新屏幕内容,使得开发基于终端的应用程序变得更加简单。`curses`库的使用主要分为以下几个核心概念: 1. **窗口管理**:curses将屏幕划分为多个窗口,每个窗口可以...
Python的curses库是一个在控制台上提供彩色和光标移动功能的模块,它使得开发者能够创建基于文本的用户界面。`python_curses-2.2.2-cp39-cp39-win_amd64.whl` 文件是这个库的一个预编译版本,专为Python 3.9构建,...
调试PyCurses程序通常涉及输出日志、使用调试器或专门的curses调试工具,以便跟踪屏幕更新和键盘事件。 总之,PyCurses为Python开发者提供了一个强大且灵活的工具,用于创建功能丰富的控制台应用程序,无论是在历史...
curses.h 头文件定义了 Curses 库的 API,提供了各种函数和宏,用于创建和管理基于文本的用户界面。curses 库是 Curses 库的核心部分,提供了各种函数,用于创建和管理基于文本的用户界面。form 库是 Curses 库的一...
Curses 提供了一系列 API 来处理文本屏幕的操作: - `initscr`: 初始化 curses 模式 - `printw`: 在屏幕指定位置打印字符串 - `getch`: 获取用户按键 - `endwin`: 结束 curses 模式 ##### 6.3 属性 除了 API 外,...
1. **curses**:curses库是此项目的核心,它提供了一系列函数,用于管理屏幕上的文本布局、颜色、键盘输入处理等功能。在curses模式下,程序可以动态地更新屏幕,创建窗口、面板,并响应用户的按键事件,使得在字符...
Curses 是一个在文本模式下运行的库,用于创建基于终端的应用程序。它提供了一组高级函数,使得程序员可以方便地处理终端的显示、输入和颜色等特性,而无需关心底层的终端控制代码。Curses-1.28.tgz 是这个库的一个...
- **文本编辑器**:如vim、nano等命令行编辑器,这些编辑器的界面都是基于curses构建的。 - **游戏开发**:如经典的《roguelike》游戏,这类游戏通常需要在有限的空间内展示大量信息。 - **脚本自动化**:编写用于...
【curses库】是UNIX环境下用于处理终端交互的高级库,尤其适合开发基于文本界面的程序。它使得程序员能够方便地控制光标移动、屏幕显示和键盘输入,从而实现滚屏、彩色打印等功能,适用于各种不同类型的终端。curses...
Linux下的curses编程是一种在基于文本的终端上创建交互式用户界面的技术,它允许程序员创建动态更新的屏幕,类似于简单的图形界面。在这个“魔岛寻宝”游戏中,curses库被用来控制屏幕上的字符输出、读取用户输入,...
尽管现在图形用户界面已经成为主流,但基于文本的用户界面仍然有着广泛的应用场景。在服务器管理、控制台应用开发等领域,curses 提供了一个强大的工具集来创建复杂的文本界面。此外,学习 curses 也有助于深入理解...