问题描述
我有一个复杂的应用程序,它的 GUI 需要与一些 I/O 设备和一些 WebAPI 进行对话。我把我的 wx.Frame 类放在主文件中,因为我读到 GUI 应该在主线程中以避免冻结
if __name__ == "__main__":
app = wx.App()
frame = Window()
app.MainLoop()
但 GUI 仍然经常冻结,有时它根本不显示,并且会出现一条消息“My_app 没有响应”。
所有 I/O 和 webAPI 管理都在由 frame
创建的单独线程中完成。唯一不在主文件中的 GUI 元素是组成我的笔记本的页面
from PageOne import PageOne
from PageTwo import PageTwo
from PageThree import PageThree
...
self.page1 = PageOne(self.nb)
self.page2 = PageTwo(self.nb)
self.page3 = PageThree(self.nb)
self.nb.AddPage(self.page1,"Page1")
self.nb.AddPage(self.page2,"Page2")
self.nb.AddPage(self.page3,"Page3")
辅助线程和 GUI 之间的所有通信都使用主文件中的 wx.lib.newevent.NewEvent()
和线程中的 wx.PostEvent(self.parent,my_evt)
完成。
我使用的是 wxpython 4.1.1 和 Ubuntu 20.04.2 LTS。
关于如何防止 GUI 不响应或冻结的任何建议?使用多处理而不是多线程可能是更好的主意吗?我知道线程通常更适合 I/O 应用程序......但在我的线程都是无循环的情况下仍然如此吗?
def run(self):
while True:
do_stuff()
解决方法
这本身不是答案。
但是,它可能有助于找到解决方案。
鉴于我们不知道 none
,您的代码在做什么,没有人可以回答这样的问题。
这相当于问,How long is a piece of string?
最好的建议是使用类似下面的代码,调整 thread
以执行类似于您的一个/多个线程正在执行的操作,并查看您是否可以复制该行为并希望找到原因。
import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event,EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class ThreadFrame(wx.Frame):
def __init__(self,title,parent=None):
wx.Frame.__init__(self,parent=parent,title=title)
panel = wx.Panel(self)
self.btn = wx.Button(panel,label='Stop Long running process',size=(200,30),pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON,self.OnExit)
self.progress = wx.Gauge(panel,size=(240,10),50),range=240)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT,self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE,self.OnExit)
self.Show()
self.mythread = TestThread(self)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self,event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self,event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
self.mythread.join() # Wait for it to finish
self.Destroy()
class TestThread(Thread):
def __init__(self,parent_target):
Thread.__init__(self)
self.parent = parent_target
self.stopthread = False
self.time = time.time()
self.start() # start the thread
def run(self):
# A loop that will run for 2 minutes then terminate
while self.stopthread == False:
curr_loop = int(time.time() - self.time)
if curr_loop < 240:
time.sleep(0.1)
evt = progress_event(count=curr_loop)
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent,evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
self.terminate()
def terminate(self):
self.stopthread = True
class MyPanel(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self,parent)
self.text_count = 0
self.thread_count = 0
self.parent=parent
btn = wx.Button(self,wx.ID_ANY,label='Start Long running process',10))
btn.Bind(wx.EVT_BUTTON,self.Thread_Frame)
btn2 = wx.Button(self,label='Is the GUI still active?',50))
btn2.Bind(wx.EVT_BUTTON,self.AddText)
self.txt = wx.TextCtrl(self,style= wx.TE_MULTILINE,90),size=(400,100))
def Thread_Frame(self,event):
self.thread_count += 1
frame = ThreadFrame(title='Threaded Task '+str(self.thread_count),parent=self.parent)
def AddText(self,event):
self.text_count += 1
txt = "Gui is still active " + str(self.text_count)+"\n"
self.txt.write(txt)
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,title='Main Frame',size=(600,400))
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()