GLUT框架下按下ESC键后类人脸图形无法按预期循环右移的问题排查与解决请求
问题分析与解决方案
首先,你的问题核心在于误用了阻塞式的Sleep和for循环。GLUT是单线程的事件驱动框架,当你在moveright函数里调用Sleep(1000)时,整个GLUT主循环会被彻底卡住,无法处理重绘请求(glutPostRedisplay的效果要等到主循环空闲时才会生效)。所以尽管你的坐标变量已经更新,但直到整个for循环执行完毕,窗口才会一次性刷新,而且最后还调用了glutDestroyWindow(1)直接关闭窗口,自然看不到任何移动过程。
修复步骤
我们需要改用GLUT的glutTimerFunc来实现逐次延迟移动,这是GLUT框架下处理定时任务的标准方式,不会阻塞主循环。具体做法如下:
- 移除
moveright里的for循环和Sleep,改成每次定时器触发时完成一次移动,然后根据剩余次数决定是否继续设置下一个定时器。 - 调整
facemove里的定时器调用,传递初始的移动次数(比如5次)。 - 按需保留或移除
glutDestroyWindow调用(如果不需要移动完成后关闭窗口,就删掉它)。
修改后的完整代码
#include <iostream> #include <cstdlib> #include <GL/glut.h> using namespace std; void display(); void facemove(unsigned char key, int x, int y); void moveright(int remainingMoves); // y axis of eyes float eyelower = 0.25f; float eyeupper = 0.50f; // left eye float leftx1 = -0.50f; float leftx2 = -0.10f; float leftx3 = -0.30f; // right eye float rightx1 = 0.10f; float rightx2 = 0.50f; float rightx3 = 0.30f; // mouth float mouthx1 = -0.15f; float mouthx2 = 0.15f; float mouthy1 = 0.0f; // default color for left eye int lrdefault = 255; int lgdefault = 215; int lbdefault = 0; // default color for right eye int rrdefault = 255; int rgdefault = 215; int rbdefault = 0; int main(int argc, char** argv){ glutInit(&argc, argv); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("My First Program"); glutDisplayFunc(display); glutKeyboardFunc(facemove); glutMainLoop(); return 0; } void display(){ glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); glColor3ub(0,102,0); glVertex2f(-0.98f, -0.98f); glVertex2f(-0.98f, 0.98f); glVertex2f(0.98f, 0.98f); glVertex2f(0.98f, -0.98f); glEnd(); glBegin(GL_TRIANGLES); glColor3ub(lrdefault, lgdefault, lbdefault); glVertex2f(leftx1, eyelower); glVertex2f(leftx2, eyelower); glVertex2f(leftx3, eyeupper); glEnd(); glBegin(GL_TRIANGLES); glColor3ub(rrdefault, rgdefault, rbdefault); glVertex2f(rightx1, eyelower); glVertex2f(rightx2, eyelower); glVertex2f(rightx3, eyeupper); glEnd(); glColor3ub(255, 215, 0); glLineWidth(5.0f); glBegin(GL_LINES); glVertex2f(mouthx1,mouthy1); glVertex2f(mouthx2, mouthy1); glEnd(); glFlush(); } void facemove(unsigned char key, int x, int y){ switch(key){ case 27: // 先延迟5秒,然后开始第一次移动,剩余5次 glutTimerFunc(5000, moveright, 5); break; default: break; } } void moveright(int remainingMoves){ if(remainingMoves <= 0){ // 移动完成,若需要可在这里销毁窗口 // glutDestroyWindow(glutGetWindow()); return; } // 移动所有面部元素的坐标 leftx1 += .09f; leftx2 += .09f; leftx3 += .09f; rightx1 += .09f; rightx2 += .09f; rightx3 += .09f; eyelower += .09f; eyeupper += .09f; mouthx1 += .09f; mouthx2 += .09f; // 请求窗口重绘 glutPostRedisplay(); cout << "已完成移动次数: " << 5 - remainingMoves + 1 << endl; // 设置下一次定时器,1秒后触发,剩余次数减1 glutTimerFunc(1000, moveright, remainingMoves - 1); }
关键改动说明
- 替换阻塞逻辑:用
glutTimerFunc替代Sleep和for循环,每次定时器触发时完成一次移动,再递归设置下一次定时器,这样不会阻塞GLUT主循环,重绘请求能及时被处理,你就能看到逐次移动的效果。 - 剩余次数跟踪:通过定时器的
value参数传递剩余移动次数,当次数为0时停止移动逻辑。 - 移除无效代码:原
facemove的default分支里,glutPostRedisplay放在break之后永远不会执行,所以直接移除了这段无效代码。
这样修改后,按下ESC键后会先等待5秒,然后每秒向右移动一次人脸,共5次,你能清晰看到每一步的移动效果。
内容的提问来源于stack exchange,提问作者Austin Gyan Marbella
相关产品推荐
相关产品推荐

