Ncurses框内文本刷新时闪烁问题解决求助
解决ncurses界面闪烁问题
问题描述
我尝试在一段会更新的文本周围绘制box,但即使占位符文本处于静态状态,界面仍会出现闪烁现象。闪烁仅出现在包含文本的行上,推测是写入文本时重绘了整行而非仅特定字符。我尝试过组合使用wrefresh()、touchwin()、erase()和refresh(),但都无法消除闪烁,请问如何解决?
代码示例
/* * @file main.cc * @author Tom Eaton * @date 09 Jan 2018 * @brief Command line interface to show Hawkcell PCB data. */ #include <iostream> #include <string> #include <cstring> #include <ncurses.h> using namespace std; /* * Prints string in the horizontal center of row specified. * @param row - Row that text should be printed on * @param mesg - Pointer to char array containing message */ void printHorizCenter(int row, char* mesg) { mvprintw(row, ((COLS - strlen(mesg))/2)-1, mesg); } /* * Reads version of PCB and updates char array with version * @param outVersion - Pointer to char array where version should be stored. * @param port - Reference to serial port */ void printHorizBlock(int row, int startCol, int endCol) { for(int i = startCol; i<=endCol; i++) { mvaddch(row, i, ACS_BLOCK); } } int main(int argc, char** argv) { //Initialise curses initscr(); cbreak(); echo(); keypad(stdscr, 1); //Make character capture non blocking nodelay(stdscr, 1); //Define char arrays for each cell char cellOne[2] = {'1', '\n'}; char cellTwo[2] = {'2', '\n'}; char cellThree[2] = {'3', '\n'}; char cellFour[2] = {'4', '\n'}; //Setup curses colour schemes start_color(); use_default_colors(); curs_set(0); //Setup curses colour pairs init_pair(1, COLOR_RED, -1); init_pair(2, COLOR_WHITE, COLOR_RED); WINDOW *win = newwin(10, 20, 1, 0); //Main loop while (1) { if(getch() == 12) { endwin(); cerr << "You pressed left and exited" << endl; exit(1); } //Print top title bar attron(COLOR_PAIR(1)); printHorizBlock(0, 0, (COLS/2) - (strlen("HawkCell")/2)); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(2)); printHorizCenter(0, (char *)"HawkCell"); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(1)); printHorizBlock(0, (COLS/2) + (strlen("HawkCell")/2) - 1, COLS); attroff(COLOR_PAIR(1)); //Print voltage data mvprintw(3, 1, "Cell 1: %.3fV\n", 4.0f); mvprintw(4, 1, "Cell 2: %.3fV\n", 4.0f); mvprintw(5, 1, "Cell 3: %.3fV\n", 4.0f); mvprintw(6, 1, "Cell 4: %.3fV\n", 4.0f); mvprintw(7, 1, "Total: %.3fV\n", 4.0f); //Print bottom info bar attron(COLOR_PAIR(1)); printHorizBlock(LINES-1, 0, COLS); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(2)); printHorizCenter(LINES-1, (char *)"ctrl+c: Exit, ctrl+l: Toggle logging "); attroff(COLOR_PAIR(2)); //Call curses refresh function to update screen box(win, 0, 0); //touchwin(win); wrefresh(win); wrefresh(stdscr); } getch(); endwin(); return 0; }
解决方案
闪烁问题在ncurses里大多是因为不必要的全屏重绘或者刷新顺序混乱,咱们可以从这几个方面调整:
1. 用双缓冲一次性刷新屏幕
ncurses的双缓冲机制可以把所有绘制操作先写到虚拟屏幕,再一次性同步到物理屏幕,避免多次局部刷新导致的闪烁。把原来的刷新代码替换成:
box(win, 0, 0); // 先把窗口更新写入虚拟缓存 wnoutrefresh(win); noutrefresh(stdscr); // 一次性同步到物理屏幕 doupdate();
这样能大幅减少屏幕的闪烁频率。
2. 静态内容只画一次
你的标题栏、底部信息栏都是不会变化的静态内容,没必要每次循环都重新绘制。把这些代码封装成函数,只在初始化时画一次,只有窗口大小改变时才重新绘制:
// 封装静态UI绘制函数 void drawStaticUI() { //Print top title bar attron(COLOR_PAIR(1)); printHorizBlock(0, 0, (COLS/2) - (strlen("HawkCell")/2)); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(2)); printHorizCenter(0, (char *)"HawkCell"); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(1)); printHorizBlock(0, (COLS/2) + (strlen("HawkCell")/2) - 1, COLS); attroff(COLOR_PAIR(1)); //Print bottom info bar attron(COLOR_PAIR(1)); printHorizBlock(LINES-1, 0, COLS); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(2)); printHorizCenter(LINES-1, (char *)"ctrl+c: Exit, ctrl+l: Toggle logging "); attroff(COLOR_PAIR(2)); } int main(int argc, char** argv) { // ... 初始化代码 ... // 第一次绘制静态UI drawStaticUI(); refresh(); //Main loop while (1) { int ch = getch(); if(ch == 12) { endwin(); cerr << "You pressed left and exited" << endl; exit(1); } // 处理窗口大小变化,重新绘制静态UI if(ch == KEY_RESIZE) { erase(); drawStaticUI(); } // 只绘制动态更新的电压数据,去掉\n避免整行重绘 mvprintw(3, 1, "Cell 1: %.3fV", 4.0f); mvprintw(4, 1, "Cell 2: %.3fV", 4.0f); mvprintw(5, 1, "Cell 3: %.3fV", 4.0f); mvprintw(6, 1, "Cell 4: %.3fV", 4.0f); mvprintw(7, 1, "Total: %.3fV", 4.0f); // 双缓冲刷新 box(win, 0, 0); wnoutrefresh(win); noutrefresh(stdscr); doupdate(); } }
注意去掉mvprintw里的\n,因为换行符会触发整行的刷新,这也是闪烁的原因之一。
3. 优化块绘制函数
你现在的printHorizBlock是逐个字符画ACS_BLOCK,效率很低。换成ncurses自带的mvwhline函数,一次性绘制整行块:
void printHorizBlock(int row, int startCol, int endCol) { // 一次性绘制从startCol到endCol的ACS_BLOCK mvwhline(stdscr, row, startCol, ACS_BLOCK, endCol - startCol + 1); }
这样能减少大量的绘制操作,降低闪烁概率。
4. 关闭不必要的回显
你的代码里开启了echo(),但如果界面不需要回显用户输入,关掉它能减少不必要的屏幕操作:
// 替换echo();为noecho(); noecho();
内容的提问来源于stack exchange,提问作者Tom Eaton
相关产品推荐
相关产品推荐

