为什么 GLUT window 不响应重新显示请求?

Why doesn't GLUT window respond to redisplay requests?

我有一个程序可以在没有 display/UI 的情况下执行大部分工作,但随后需要以低延迟方式短暂显示一些内容。我通过 PyOpenGL 使用 GLUT 在单独的线程中显示 window ,然后在必要时用主线程中的内容填充它。我遇到的问题是 GLUT window 似乎只在鼠标光标移过 window 时才识别出已发出重新显示请求。这看起来很奇怪,显然我希望 GLUT window 尽快更新,而不是无限期地等待鼠标光标移动。

我怀疑这可能是特定于平台的问题。我已确认该行为可在 Ubuntu 16.04.2 和 14.04.5 上重现。

要重现的完整代码如下(需要 PyOpenGL)。此代码所需的行为是让 GLUT window 显示黑色,然后在一秒钟后自动切换为红色,然后是绿色,然后是蓝色,然后退出,所有这些在脚本启动后都没有任何鼠标移动。相反,GLUT window 将在程序启动时显示黑色,并且它将无限期地保持黑色,直到鼠标光标移过 window(然后变为红色,直到再次移动鼠标,等等)。光标在 window 内部还是外部都没有关系——它必须被移动,否则 onDisplay 处理程序将不会触发。

如何让下面的程序 运行 完成而不管鼠标在做什么?

from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
import sys
import threading
import time

hWin = None
displayevent = threading.Event()
bgcolor = (0,0,0,1)

def onDisplay():
    print("  [onDisplay] Started")
    glClearColor(*bgcolor)
    glClear(GL_COLOR_BUFFER_BIT)
    glutSwapBuffers()
    print("  [onDisplay] Finished")
    displayevent.set()
    print("  [onDisplay] Exiting")

def runGl():
    global hWin

    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
    glutInitWindowPosition(0, 0)
    hWin = glutCreateWindow("GLWindow")

    glutDisplayFunc(onDisplay)

    print("[runGl] Entering main GLUT loop")
    glutMainLoop()
    print("[runGl] Exited main GLUT loop")


glThread = threading.Thread(target=runGl)
print("[main] Showing GLUT window")
glThread.start()
print("[main] Waiting for GLUT window to initialize")
time.sleep(1)
print("[main] GLUT window initialization (assumed) complete")

colors = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)]

for i in range(len(colors)):
    print("[main] --- Iteration %d ---" % i)
    print("[main] Waiting...")
    time.sleep(1)
    print("[main] Trigging display update")
    bgcolor = colors[i]
    displayevent.clear()
    glutPostRedisplay()
    print("[main] Waiting for redraw to complete")
    displayevent.wait()
    print("[main] Redraw is complete")

print("[main] Closing window")
glutDestroyWindow(hWin)
print("[main] All done.")

您必须从初始化 GLUT 的同一线程调用 glutPostRedisplay()

How can I call glutPostRedisplay on the thread that is fully occupied by glutMainLoop

反过来想一想。使用该事件告诉您的主循环发生了某些变化。

def display():
    glClearColor(*bgcolor)
    glClear(GL_COLOR_BUFFER_BIT)
    glutSwapBuffers()

def idle():
    if displayevent.is_set():
        glutPostRedisplay()

使用glutIdleFunc(idle).

这样您的主循环将继续 运行 并检查是否设置了 displayevent。然后在你的循环中而不是 displayevent.wait() 你调用 displayevent.set().

您当然可以在调用 glutSwapBuffers() 后设置一个额外的事件。然后你可以等待那个事件。

这与您执行异步加载的方式类似。您有一个辅助线程加载并处理图像数据。然后当它完成时你将它标记为这样并且主/渲染线程调用 glTexImage2D().