窗口是 curses 中最重要的概念。您已经在上面看到了标准窗口 stdscr,所有函数都隐式地在该窗口上操作。现在,为了设计即使是最简单的 GUI,您也需要求助于窗口。您可能想要使用窗口的主要原因是为了分别操作屏幕的各个部分,从而提高效率,仅更新需要更改的窗口,并为了更好的设计。我会说最后一个原因是在使用窗口时最重要的。您应该始终在程序中力求更好且易于管理的设计。如果您正在编写大型、复杂的 GUI,那么在您开始做任何事情之前,这一点至关重要。
可以通过调用以下函数来创建窗口newwin()。它实际上并没有在屏幕上创建任何东西。它为结构分配内存以操作窗口,并使用有关窗口的数据(如窗口的大小、beginy、beginx 等)更新结构。因此,在 curses 中,窗口只是一个假想窗口的抽象,可以独立于屏幕的其他部分进行操作。函数 newwin() 返回指向 WINDOW 结构的指针,该指针可以传递给窗口相关函数,如 wprintw() 等。最后,可以使用 delwin() 销毁窗口。它将释放与窗口结构关联的内存。
如果创建了一个窗口而我们看不到它,那有什么乐趣呢?因此,有趣的部分从显示窗口开始。函数box()可用于在窗口周围绘制边框。让我们在这个例子中更详细地探讨这些函数。
示例 7. 窗口边框示例
#include <ncurses.h>
WINDOW *create_newwin(int height, int width, int starty, int startx);
void destroy_win(WINDOW *local_win);
int main(int argc, char *argv[])
{ WINDOW *my_win;
int startx, starty, width, height;
int ch;
initscr(); /* Start curses mode */
cbreak(); /* Line buffering disabled, Pass on
* everty thing to me */
keypad(stdscr, TRUE); /* I need that nifty F1 */
height = 3;
width = 10;
starty = (LINES - height) / 2; /* Calculating for a center placement */
startx = (COLS - width) / 2; /* of the window */
printw("Press F1 to exit");
refresh();
my_win = create_newwin(height, width, starty, startx);
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case KEY_LEFT:
destroy_win(my_win);
my_win = create_newwin(height, width, starty,--startx);
break;
case KEY_RIGHT:
destroy_win(my_win);
my_win = create_newwin(height, width, starty,++startx);
break;
case KEY_UP:
destroy_win(my_win);
my_win = create_newwin(height, width, --starty,startx);
break;
case KEY_DOWN:
destroy_win(my_win);
my_win = create_newwin(height, width, ++starty,startx);
break;
}
}
endwin(); /* End curses mode */
return 0;
}
WINDOW *create_newwin(int height, int width, int starty, int startx)
{ WINDOW *local_win;
local_win = newwin(height, width, starty, startx);
box(local_win, 0 , 0); /* 0, 0 gives default characters
* for the vertical and horizontal
* lines */
wrefresh(local_win); /* Show that box */
return local_win;
}
void destroy_win(WINDOW *local_win)
{
/* box(local_win, ' ', ' '); : This won't produce the desired
* result of erasing the window. It will leave it's four corners
* and so an ugly remnant of window.
*/
wborder(local_win, ' ', ' ', ' ',' ',' ',' ',' ',' ');
/* The parameters taken are
* 1. win: the window on which to operate
* 2. ls: character to be used for the left side of the window
* 3. rs: character to be used for the right side of the window
* 4. ts: character to be used for the top side of the window
* 5. bs: character to be used for the bottom side of the window
* 6. tl: character to be used for the top left corner of the window
* 7. tr: character to be used for the top right corner of the window
* 8. bl: character to be used for the bottom left corner of the window
* 9. br: character to be used for the bottom right corner of the window
*/
wrefresh(local_win);
delwin(local_win);
} |
不要尖叫。我知道这是一个很大的例子。但我必须在这里解释一些重要的事情:-) 。这个程序创建了一个矩形窗口,可以使用左、右、上、下箭头键移动。它在用户按下按键时重复创建和销毁窗口。不要超出屏幕限制。检查这些限制留给读者作为练习。让我们逐行剖析它。
以下create_newwin()函数创建一个窗口,并使用 box 在其周围显示边框。函数newwin()和使用 box 在其周围显示边框。函数destroy_win()首先通过用 ' ' 字符绘制边框来从屏幕上擦除窗口,然后调用delwin()以释放与其相关的内存。根据用户按下的键,starty 或 startx 会被更改,并创建一个新窗口。
在 destroy_win 中,正如您所见,我使用了 wborder 而不是 box。原因写在注释中(您错过了。我知道。阅读代码:-))。wborder 使用给定的 4 个角点和 4 条线在窗口周围绘制边框。为了清楚地说明,如果您像下面这样调用了 wborder
wborder(win, '|', '|', '-', '-', '+', '+', '+', '+'); |
它会产生类似这样的东西
+------------+ | | | | | | | | | | | | +------------+ |
您还可以在上面的示例中看到,我使用了变量 COLS、LINES,它们在 initscr() 之后被初始化为屏幕尺寸。它们可用于查找屏幕尺寸并找到屏幕的中心坐标,如上所示。函数getch()像往常一样从键盘获取按键,并根据按键执行相应的工作。这种类型的 switch-case 在任何基于 GUI 的程序中都非常常见。
上面的程序非常低效,因为每次按下按键,都会销毁一个窗口并创建另一个窗口。因此,让我们编写一个更高效的程序,它使用其他边框相关函数。
以下程序使用mvhline()和mvvline()来实现类似的效果。这两个函数很简单。它们在指定位置创建指定长度的水平线或垂直线。
示例 8. 更多边框函数
#include <ncurses.h>
typedef struct _win_border_struct {
chtype ls, rs, ts, bs,
tl, tr, bl, br;
}WIN_BORDER;
typedef struct _WIN_struct {
int startx, starty;
int height, width;
WIN_BORDER border;
}WIN;
void init_win_params(WIN *p_win);
void print_win_params(WIN *p_win);
void create_box(WIN *win, bool flag);
int main(int argc, char *argv[])
{ WIN win;
int ch;
initscr(); /* Start curses mode */
start_color(); /* Start the color functionality */
cbreak(); /* Line buffering disabled, Pass on
* everty thing to me */
keypad(stdscr, TRUE); /* I need that nifty F1 */
noecho();
init_pair(1, COLOR_CYAN, COLOR_BLACK);
/* Initialize the window parameters */
init_win_params(&win);
print_win_params(&win);
attron(COLOR_PAIR(1));
printw("Press F1 to exit");
refresh();
attroff(COLOR_PAIR(1));
create_box(&win, TRUE);
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case KEY_LEFT:
create_box(&win, FALSE);
--win.startx;
create_box(&win, TRUE);
break;
case KEY_RIGHT:
create_box(&win, FALSE);
++win.startx;
create_box(&win, TRUE);
break;
case KEY_UP:
create_box(&win, FALSE);
--win.starty;
create_box(&win, TRUE);
break;
case KEY_DOWN:
create_box(&win, FALSE);
++win.starty;
create_box(&win, TRUE);
break;
}
}
endwin(); /* End curses mode */
return 0;
}
void init_win_params(WIN *p_win)
{
p_win->height = 3;
p_win->width = 10;
p_win->starty = (LINES - p_win->height)/2;
p_win->startx = (COLS - p_win->width)/2;
p_win->border.ls = '|';
p_win->border.rs = '|';
p_win->border.ts = '-';
p_win->border.bs = '-';
p_win->border.tl = '+';
p_win->border.tr = '+';
p_win->border.bl = '+';
p_win->border.br = '+';
}
void print_win_params(WIN *p_win)
{
#ifdef _DEBUG
mvprintw(25, 0, "%d %d %d %d", p_win->startx, p_win->starty,
p_win->width, p_win->height);
refresh();
#endif
}
void create_box(WIN *p_win, bool flag)
{ int i, j;
int x, y, w, h;
x = p_win->startx;
y = p_win->starty;
w = p_win->width;
h = p_win->height;
if(flag == TRUE)
{ mvaddch(y, x, p_win->border.tl);
mvaddch(y, x + w, p_win->border.tr);
mvaddch(y + h, x, p_win->border.bl);
mvaddch(y + h, x + w, p_win->border.br);
mvhline(y, x + 1, p_win->border.ts, w - 1);
mvhline(y + h, x + 1, p_win->border.bs, w - 1);
mvvline(y + 1, x, p_win->border.ls, h - 1);
mvvline(y + 1, x + w, p_win->border.rs, h - 1);
}
else
for(j = y; j <= y + h; ++j)
for(i = x; i <= x + w; ++i)
mvaddch(j, i, ' ');
refresh();
} |