如何连续更新glut窗口?

14
我有一个实际的机器人,用于控制OpenGL中的虚拟机器人。我希望在线展示我的主机器人(真实机器人)对从属机器人(OpenGL中的虚拟机器人)的每个动作的控制,因此需要不断更新glut窗口,实际上只要主机器人移动了,从属机器人也会移动,所有这些动作都应该是在线的。
我总是使用getData函数从主机器人中获取数据,但我不知道如何更新窗口。
以下是我的代码:

********************************************/

  void OnIdle(void){  
    initSocket();

  printf("\n  Defining Step Time Parameters and Initial Conditions for solving Dynamic equations\n");

  xi=0;
  xf=0.1;
  printf("\n    end value x         : %f ",xf); 
  i=0;  yi[i]=0; 
  i++;yi[i]=-1.570796;
  i++;yi[i]=-1.570796;
  i++;yi[i]=0;
  i++;yi[i]=0;
  i++;yi[i]=0;
  ndata=2; fi=1;

  double counter=0.1;

  Eqdifp(v1,v2,v3,v4,v5,v6,xi,xf,yi,ndata,p,fi);


  for(int i=0;i<50;i++)
    //while(1)
  {

      getData();

      printf("\n");
      for(int i=0;i<6; i++)
      {

          printf("%d = %.3f\n", i,drecvbuf[i]);
      }
      printf("\n");

   yi[0]=v1[ndata];
   yi[1]=v2[ndata];
   yi[2]=v3[ndata];
   yi[3]=v4[ndata];
   yi[4]=v5[ndata];
   yi[5]=v6[ndata];
    printf("my nadata %f\n",v1[ndata]);
    counter=counter+0.1;

    Eqdifp(v1,v2,v3,v4,v5,v6,xi,xf,yi,ndata,p,fi);
    glutPostRedisplay();
 }
  }
/////////////////////////////////////////////////////
  int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(900,500);
    int u=glutCreateWindow("3DOF robot");
    myinit();
    createMenu();
    glutIdleFunc (OnIdle);
    glutDisplayFunc(Display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(KeyDown);

    glutMainLoop(); 

    System::Timers::Timer^ aTimer = gcnew System::Timers::Timer( 100 );

      // Hook up the Elapsed event for the timer.
    aTimer->Elapsed += gcnew System::Timers::ElapsedEventHandler( OnTimedEvent );

      // Set the Interval to 2 seconds (2000 milliseconds).
    aTimer->Enabled = true;
    return 0;

  }

不想打标签,因为我不熟悉,但是 System::Timers::Timer^ aTimer = gcnew System::Timers::Timer( 100 ); 对我来说似乎是 C++/CLI? - Bart
只要你看到 ^,它就肯定是 C++/CLI。 - Christian Rau
实际上,gcnew 更能表明 C++/CLI。^ 是标准 C++ 中的有效运算符。 - Ben Voigt
@Ben 是的,你说得对,我没有考虑过异或。 - Christian Rau
3个回答

15

在更新完数据后,您可以调用glutPostRedisplay函数来安排窗口重绘(当然要使用GLUT的display func),这样只要它返回消息队列,窗口就会被重新绘制。

但是如果您在一个无限循环中连续轮询机器人数据,那么这种方法将不起作用,因为它会不断地阻塞程序。您应该使用定时器在短时间间隔内计划机器人更新,以便在这些更新之间,程序可以返回到主事件循环并重新绘制窗口。或者您可以调用一些函数,告诉框架访问事件循环。您的代码示例目前并没有真正解释您如何做到这一点(或者我不熟悉您调用的函数)。


亲爱的Christin,谢谢你的回答。 - Likak
实际上,我对OpenGL甚至C++都很新手,不是很专业。 - Likak
所以,我更喜欢使用OpenGL的函数,就像你说的那个glutPostRedisplay一样,但我不知道该怎么做。我需要一些不太复杂的东西! - Likak
我编辑了上面的代码,尝试了建议的方法,但是它没有起作用,我感到困惑:(( - Likak
顺便说一下,glutPostRedisplay并不属于OpenGL,而是GLUT。OpenGL是用于编程图形硬件(显示事物)的接口,而GLUT是一个GUI抽象库,使简单的OpenGL程序的创建更容易。但OpenGL与GUI或事件处理等任务无关。 - Christian Rau

4

GLUT为您提供了一个空闲回调函数(void (*)(void)签名),通过glutIdleFunc设置。在空闲处理程序中检索机器人输入数据。或者使用一个单独的线程轮询数据,填充数据结构;使用信号量在新数据到达后解锁空闲,使用带超时的锁定使您的程序保持交互性。伪代码:

Semaphore robot_data_semaphore;

void wait_for_data(void)
{
    SemaphoreLockStatus lock_status = 
        semaphore_raise_timeout(robot_data_semaphore, RobotDataTimeout);
    if( lock_status == SEMAPHORE_RAISED ) {
        update_scene_with_robot_data();
        semaphore_lower(robot_data_semaphore);
        glutPostRedisplay();
    }
}

void main(int argc, char *argv[])
{
/* ... */
    semaphore_init(robot_data_semaphore);
    Thread thread_robot_data_poller = thread_create(robot_data_poller);
    glutIdleFunc(wait_for_data);

/* ... */
    thread_start(thread_robot_data_poller);
    glutMainLoop();
}

我按照你说的尝试了,但是没有起作用,我修改了上面的代码,并且按照你的方法进行了操作,你能帮忙看一下,看看问题出在哪里吗?非常感谢!:) - Likak
1
@bahare:空闲处理程序必须在大约50毫秒内返回,以保持程序的响应性。此外,在空闲处理程序返回之前,显示更新不会发生。glutPostRedisplay只是设置了一些标志。在空闲处理程序中,您应该执行事件处理循环的默认分支中所放置的操作 - 但没有循环。 - datenwolf
你能帮我纠正这些代码吗?我尝试了很多方法,但是都没成功。 - Likak
@bahare:为此,我需要完整的源代码,因为您发布的部分信息不足。 - datenwolf

0
我会这样做。把glutMainLoop()作为你的循环,每次处理一个getData()并绘制它,速度比你想象的要快。
要实现“连续”更新,需要执行以下操作:
  1. 处理数据(getData()然后进行计算)
  2. 重新绘制(Display() glut在每次循环时调用此函数)
  3. 使用glut_____Func()定义其他函数
  4. 回到步骤1
Glut会一直运行,直到程序退出。
//called every time glutMainLoop
//do data processing
void OnIdle(void)
{  
    getData();

    printf("\n");
    for(int i=0;i<6; i++)
    {
        printf("%d = %.3f\n", i,drecvbuf[i]);
    }
    printf("\n");

    yi[0]=v1[ndata];
    yi[1]=v2[ndata];
    yi[2]=v3[ndata];
    yi[3]=v4[ndata];
    yi[4]=v5[ndata];
    yi[5]=v6[ndata];
    printf("my nadata %f\n",v1[ndata]);

    Eqdifp(v1,v2,v3,v4,v5,v6,xi,xf,yi,ndata,p,fi);
}
//also called every loop of glutMainLoop
void Display()
{
    ...
    //Your previous Display() function just add this:
    glutPostRedisplay(); //everytime you are done 
                        // drawing you put it on the screen
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(900,500);
    int u=glutCreateWindow("3DOF robot");
    myinit();
    createMenu();
    glutIdleFunc (OnIdle);
    glutDisplayFunc(Display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(KeyDown);

    ///////////////
    // SETUP YOUR INITIAL DATA
    System::Timers::Timer^ aTimer = gcnew System::Timers::Timer( 100 );

    // Hook up the Elapsed event for the timer.
    aTimer->Elapsed += gcnew System::Timers::ElapsedEventHandler( OnTimedEvent );

    // Set the Interval to 2 seconds (2000 milliseconds).
    aTimer->Enabled = true;

    initSocket();

    printf("\n  Defining Step Time Parameters and Initial Conditions for solving Dynamic equations\n");

    xi=0;
    xf=0.1;
    printf("\n    end value x         : %f ",xf); 
    i=0;  yi[i]=0; 
    i++;yi[i]=-1.570796;
    i++;yi[i]=-1.570796;
    i++;yi[i]=0;
    i++;yi[i]=0;
    i++;yi[i]=0;
    ndata=2; fi=1;

    Eqdifp(v1,v2,v3,v4,v5,v6,xi,xf,yi,ndata,p,fi);
    //////////////

    //Start the Main Loop
    glutMainLoop(); //This statement blocks, meaning that until you exit the 
                    // glut main loop no statments past this point will be executed.


    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接