9

我正在尝试使用 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;
}
4

3 回答 3

15

你的第一直觉是正确getch()的:隐含的refresh(). 具体来说,getch()等同于wgetch(stdscr),因此它是一种隐式的wrefresh(stdscr)——更新一个stdscr您不使用的窗口 ( ),它恰好填满了屏幕。从那时起后续调用没有影响的原因是,stdscr就诅咒而言,它已经是最新的,因为在那之后你再也没有写过它(不要介意它的内容已经在实际屏幕上被覆盖) .

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

于 2014-03-01T23:33:02.803 回答
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。

从 pdcurses 文档

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

所以调用:

nodelay(stdscr, TRUE);

如果你想getch()不阻塞;ERR如果没有按下任何键,它将返回。

于 2013-12-31T07:00:58.827 回答
-1

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。让你的w​​hile循环像这样:

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 的使用“无用”,除了你仍然可以在控制台中做一些快速图形的事实,这可以无需使用任何库,只需一点技巧即可制作。如果您在该菜单中制作一些动画,例如由键盘驱动的移动光标等,您就会明白我的意思。基本上使用curses主要是因为它的延迟能力,让控制台中的东西看起来更自然,所以它们不会闪烁,尤其是当涉及到通过重复循环生成的细节/动画时。

于 2013-12-31T06:37:22.190 回答