如何解决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 举报,一经查实,本站将立刻删除。