wx.Python 杀死子线程

问题描述

我有一个 GUI 应用程序,在我的主线程中运行,一些子线程在后台执行操作。我将关闭按钮绑定到一个方法,在关闭软件之前做一些事情,但我不知道如何停止子线程

self.Bind(wx.EVT_CLOSE,self.onCloseFrame)

def onCloseFrame(self,event):
    do_stuff()
    # stop threads here
    self.Destroy()

有没有办法做到这一点?

解决方法

这是我模拟的一个测试。
它比实际需要的要复杂一些,因为我想要多个进度指示器,但您应该能够了解其要点。

包含一个终止函数并触发它,如果您希望在线程自然完成之前结束它。
在这种情况下,线程在一分钟后超时,但它可以做任何事情。

点击 Start 以查看您希望同时运行的多个线程。

import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event,EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()

class ProcessingFrame(wx.Frame):

    def __init__(self,title,parent=None):
        wx.Frame.__init__(self,parent=parent,title=title)
        panel = wx.Panel(self)
        self.parent = parent
        self.btn = wx.Button(panel,label='Stop processing',size=(200,30),pos=(10,10))
        self.btn.Bind(wx.EVT_BUTTON,self.OnExit)
        self.progress = wx.Gauge(panel,size=(240,10),50),range=60)
        self.process = wx.TextCtrl(panel,size = (200,250),100),style = wx.TE_MULTILINE)
        #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)

    def OnProgress(self,event):
        self.progress.SetValue(event.count)
        self.process.write("Checking process: "+event.process+"\n")
        self.Refresh()
        if event.count >= 60:
            self.OnExit(None)

    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.process = 1 # Testing only - mock process id
        self.start()    # start the thread

    def run(self):
        # A loop that will run for a while then terminate
        while self.stopthread == False:
            curr_loop = int(time.time() - self.time)
            self.process += 10 # Testing only - mock process id
            if curr_loop <= 60: # Update progress bar
                time.sleep(0.1)
                evt = progress_event(count=curr_loop,process=str(self.process))
                #Send back current count for the progress bar
                try:
                    wx.PostEvent(self.parent,evt)
                except: # The parent frame has probably been destroyed
                    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.parent=parent
        btn_start = wx.Button(self,wx.ID_ANY,label='Start Long running process',size=(180,10))
        btn_test = wx.Button(self,label='Is the GUI still active?',50))
        self.txt = wx.TextCtrl(self,style= wx.TE_MULTILINE,90),size=(400,100))
        btn_end = wx.Button(self,label='Stop All',size=(100,200))

        btn_start.Bind(wx.EVT_BUTTON,self.Start_Process)
        btn_test.Bind(wx.EVT_BUTTON,self.ActiveText)
        btn_end.Bind(wx.EVT_BUTTON,self.End_AllProcesses)

        self.activeframes = [] # 1 per live process

    def Start_Process(self,event):
        #thread_count = active_count()
        thread_count = len(self.activeframes) + 1
        self.activeframes.append(ProcessingFrame(title='Threaded Task '+str(thread_count),parent=self))

    def End_AllProcesses(self,event):
        while len(self.activeframes) > 0:
            try:
                self.activeframes[-1].OnExit(None)
                self.activeframes.pop(-1)
            except: # process may have finished or been stopped individually
                self.activeframes.pop(-1)

    def ActiveText(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()

enter image description here