wxPython线程化-在GLCanvas中制作动画时GUI冻结

问题描述

在不冻结GUI的情况下刷新GLCanvas(具有3D几何图形)的正确方法是什么?

我正在尝试使用一组GUI元素(按钮,复选框)来控制GLCanvas,后者会刷新以显示3D动画。我的问题是动画循环打开时,GUI元素没有响应。

我尝试以三种不同的方式启动动画循环:

  • 选项1:在线程内
  • 选择2:使用wx.lib.delayedresult(很可能类似于线程)
  • 选项3:在Refresh事件之后调用onDraw

似乎我可以使用选项1,但是我需要在调用画布time.sleep(xxx)之间引入Refresh的睡眠延迟。否则,GUI的响应速度仍然很差:调整窗口大小,最终会“放牧” GUI元素,单击复选框将触发事件,但不会触发复选框的“检查”,按钮上的“鼠标悬停”效果不起作用,等等。

我附上一个小例子。在我的实际应用程序中,我使用moderngl为3D几何图形制作了动画,并且我注意到所需的“睡眠”时间取决于几何图形的沉重程度(与此示例相反,该示例极其轻巧,并且延迟小至0.00001 s)。我想知道我缺少什么。谢谢!

import wx
from wx import glcanvas
import wx.lib.delayedresult as delayedresult
from OpenGL.GL import *
import OpenGL.GL.shaders
import time
import numpy as np
from threading import Thread

# --- Option 1: Thread
class TestThread(Thread):
    def __init__(self,parent,canvas):
        Thread.__init__(self)
        self.parent=parent
        self.canvas=canvas
        self.start()    # start the thread

    def run(self):
        print('Thread running... ')
        while self.canvas.animate:
            #time.sleep(0.01) # <<<<<<<<<<<< This line needed
            self.canvas.Refresh()
        print('Tread done ')

class OpenGLCanvas(glcanvas.GLCanvas):
    def __init__(self,parent):
        glcanvas.GLCanvas.__init__(self,-1,size=(400,400))
        self.context = glcanvas.GLContext(self)
        self.SetCurrent(self.context)
        self.init = False
        self.animate = False
        self.refreshAfter = False
        self.t=0
        self.Bind(wx.EVT_PAINT,self.OnPaint)

    def OnPaint(self,event):
        wx.PaintDC(self)
        if not self.init:
            self.InitGL()
            self.init = True
        self.OnDraw()

    def InitGL(self):
        glEnable(GL_DEPTH_TEST)

    def OnDraw(self):
        """ Called at every frame"""
        glClearColor(0.1,0.0,np.mod(self.t,1),1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        if self.animate:
            self.t+=0.0005 # increment time 
            # ---- Option 3
            if self.refreshAfter: 
                self.Refresh() # Trigger next frame
        self.SwapBuffers()

    # --- Option 2: delayed results
    def onAnimDelayedEnd(self,thread):
        """ Consumer """
        print('Delayed result done')
        jobID = thread.getJobID()
        result = thread.get()

    def onAnimDelayedStart(self):
        print('Delayed result running... ')
        while self.animate:
            self.Refresh()


class MyPanel(wx.Panel):
    def __init__(self,parent):
        wx.Panel.__init__(self,parent)
        self.canvas = OpenGLCanvas(self)
        # GUI
        self.rot_btn = wx.Button(self,label="Toggle animation",pos=(430,10))
        self.cbDo  = wx.CheckBox   (self,label="Do something",100))
        self.radDo = wx.RadioButton(self,140))
        self.radDo2= wx.RadioButton(self,180))
        # Binding
        self.rot_btn.Bind(wx.EVT_BUTTON,self.toggleAnim)
        self.radDo.Bind(wx.EVT_RAdioBUTTON,self.doSomething)
        self.radDo2.Bind(wx.EVT_RAdioBUTTON,self.doSomething)
        self.cbDo.Bind(wx.EVT_CHECKBox,self.doSomething)

    def toggleAnim(self,event):
        if not self.canvas.animate:
            self.canvas.animate = True
            # --- Option 1: thread
            TestThread(self,self.canvas)
            # --- Option 2: delayed result
            #delayedresult.startWorker(self.canvas.onAnimDelayedEnd,self.canvas.onAnimDelayedStart,jobID=1)
            # --- Option 3: refreshloop
            #self.canvas.refreshAfter=True
            #self.canvas.Refresh() # set the canvas into an "infinite" refresh loop
        else:
            self.canvas.animate = False

    def doSomething(self,event):
        print('Do something')

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,None,title="My wx frame",size=(600,400))
        self.Bind(wx.EVT_CLOSE,self.on_close)
        self.panel = MyPanel(self)

    def on_close(self,event):
        self.Destroy()

if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame().Show()
    app.MainLoop()

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)