curses库:为什么getch()清除我的屏幕?

时间:2013-11-03 00:51:32

标签: c++ curses getch pdcurses

我正在尝试使用C ++学习curses库(pdcurses,就像我在Windows操作系统中一样)。 我有一个显示3个窗口的程序,然后是一个while循环,根据getch()捕获的按键进行一些处理。按下F1键时,循环退出。

然而,尽管使用wrefresh()刷新了所有三个窗口,但在进入第一次按键之前没有任何内容出现。没有while循环,一切都显示正常。我做了很多测试,这就像第一次调用getch()将完全清除屏幕,而不是后续屏幕。

我的问题是:我错过了什么?起初,我在想,也许getch()调用了一个隐式的refresh(),但为什么后续调用它没有相同的行为呢?

非常感谢您的帮助。

这是代码。

#include <curses.h>

int main()
{
    initscr();
    raw();
    keypad(stdscr, TRUE);
    noecho();
    curs_set(0);

    WINDOW *wmap, *wlog, *wlegend;
    int pressed_key;
    int map_cursor_y = 10, map_cursor_x = 32;

    wlog = newwin(5, 65, 0, 15);
    wlegend = newwin(25, 15, 0, 0);
    wmap = newwin(20, 65, 5, 15);

    box(wmap, 0 , 0);
    box(wlog, 0 , 0);
    box(wlegend, 0 , 0);

    mvwprintw(wlog, 1, 1, "this is the log window");
    mvwprintw(wlegend, 1, 1, "legends");
    mvwaddch(wmap, map_cursor_y, map_cursor_x, '@');

    wrefresh(wlog);
    wrefresh(wmap);
    wrefresh(wlegend);

    while ((pressed_key = getch()) != KEY_F(1))
    {
         /* process keys to move the @ cursor (left out because irrelevant) */

         box(wmap, 0 , 0);
         box(wlog, 0 , 0);
         box(wlegend, 0 , 0);
         wrefresh(wmap);
         wrefresh(wlog);
         wrefresh(wlegend);
    }

    endwin();
    return 0;
}

3 个答案:

答案 0 :(得分:10)

您的第一直觉是正确的:getch()执行隐式refresh()。具体来说,getch()相当于wgetch(stdscr),因此它是一个隐含的wrefresh(stdscr) - 更新您未使用的窗口(stdscr),恰好填写屏幕。后续调用从那一点开始没有影响的原因是stdscr已经是最新的,就诅咒而言,因为你之后从未写过它(更别提它的内容已被覆盖)在实际屏幕上。)

解决方案是在开始绘图之前在顶部显式调用refresh();或者,我的偏好是,在另一个窗口(以最合适的为准)上调用wgetch()而不是getch(),并完全忽略stdscr的存在。请记住,所有不允许您指定窗口的函数 - getch()refresh()等 - 实际上是对其“w”等效项的调用,stdscr为隐式窗口参数。

答案 1 :(得分:1)

默认情况下getch()会阻止,直到按下某个键。将您的循环更改为do {} while();循环:

pressed_key = /* some value that will be benign or indicate that nothing has been pressed */

do {
     /* process keys to move the @ cursor (left out because irrelevant) */

     box(wmap, 0 , 0);
     box(wlog, 0 , 0);
     box(wlegend, 0 , 0);
     wrefresh(wmap);
     wrefresh(wlog);
     wrefresh(wlegend);
} while ((pressed_key = getch()) != KEY_F(1));

如果您需要getch()非阻塞,那么这样做的方法是在默认窗口中为nodelay模式设置curses。

From the pdcurses docs

  

使用getch()wgetch()mvgetch()mvwgetch()   函数,从与之关联的终端读取字符   窗口。在nodelay模式下,如果没有输入等待,则值ERR   退回。在延迟模式下,程序将挂起直到系统   将文本传递给程序。

所以打电话:

nodelay(stdscr, TRUE);

如果您希望getch()无阻塞;如果没有按下任何键,它将返回ERR

答案 2 :(得分:0)

getch()不会清除你的屏幕,只是做它做的事情,阻止你的while循环,等待从键盘上获取一个角色。

所以这里可以解决你的问题。在curses.h之前,包括conio.h,然后像这样制作你的while循环:

do
{
box(wmap, 0 , 0);
 box(wlog, 0 , 0);
 box(wlegend, 0 , 0);
 wrefresh(wmap);
 wrefresh(wlog);
 wrefresh(wlegend);
 if(kbhit())
    pressed_key = getch();
}while (pressed_key  != KEY_F(1));

这是你的另一个解决方案,这也将使@Kaz感到高兴。这次我们将使用windows.h而不是conio.h,你不再需要那个pressed_key了。像这样制作你的while循环:

do
{
     /* process keys to move the @ cursor (left out because irrelevant) */
     box(wmap, 0 , 0);
     box(wlog, 0 , 0);
     box(wlegend, 0 , 0);
     wrefresh(wmap);
     wrefresh(wlog);
     wrefresh(wlegend);

}
while (!GetAsyncKeyState(VK_F1));

顺便说一句,使用nodelay,如另一个答案中所建议的那样,将解决当前的问题,但它几乎会使curses.h“无用”,除了你仍然可以在控制台中做一些快速图形的事实,这可以通过一些技巧而不使用任何库来完成。如果您在该菜单中制作一个小动画,就像键盘驱动的移动光标等,你会明白我的意思。基本上诅咒主要是因为它的延迟功能,在控制台中看起来更自然,所以它们不会闪烁,特别是当涉及通过重复循环产生的细节/动画时。