如何使用wx.CallAfter()返回的条件终止线程?

时间:2017-08-15 19:26:16

标签: multithreading python-2.7 wxpython progress-bar

我是wxPython的新手,也不熟悉线程概念。如果有人能提供我的问题的信息来源或建议,我将非常感激。

我使用wxpython创建了一个GUI,允许用户使用输入运行我的脚本。由于一步需要20分钟才能运行,因此我计划创建一个进度对话框以显示用户进度并允许他们中止。我使用下面的示例代码对此进行了测试。

但是,即使我点击进度对话框中的停止按钮,我也无法停止WorkThread。我试过了  1.使用pb.sendMessage()的返回值创建if语句  2.在WorkThread启动时创建ProgressDialog对象并调用ProgressDialog.abort 但它们都不起作用。我想知道是否存在实现此类代码的概念错误以实现我想要做的事情?或者,如果这可以纠正?任何提示都将不胜感激!!

class WorkThread(Thread):

    def __init__(self):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.start()  # start the thread

    def run(self):

        for i in range(10):
            time.sleep(1)
            val = 100 / 10
            wx.CallAfter(pub.sendMessage, "update", step=val)
        print 'Finish Run'

class ProgressDialog(wx.Dialog):
    def __init__(self):

        wx.Dialog.__init__(self, None)
        self.abort = False
        self.progress = 0

        bSizer2 = wx.BoxSizer(wx.VERTICAL)
        self.gauge = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge.SetValue(0)

        self.m_button1 = wx.Button(self, wx.ID_ANY, u"Stop Training", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.gauge, 0, 0, 5)
        bSizer2.Add(self.m_button1, 0, 0, 5)

        self.SetSizer(bSizer2)
        self.Layout()
        self.Centre(wx.BOTH)

        ## Connect Events

        self.m_button1.Bind(wx.EVT_BUTTON, self.on_cancel)
        pub.subscribe(self.updateProgress, "update")

    def updateProgress(self, step):
        self.progress += step

        if self.abort:
            self.Update()
            self.Close()
        elif self.progress >= 100:
            self.gauge.SetValue(self.progress)
            self.Update()
            self.Close()
        else:
            self.gauge.SetValue(self.progress)

    def on_cancel(self, event):
        """Cancels the conversion process"""
        self.abort = True
        print 'Click'
        # pub.unsubscribe(self.if_abort, 'cancel')

    def __del__(self):
        pass


########################################################################################

class MainFrame(wx.Frame):
    # ----------------------------------------------------------------------
    def __init__(self,parent):
        wx.Frame.__init__(self,parent)
        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        self.btn = btn = wx.Button(panel, label="Start Thread")
        btn.Bind(wx.EVT_BUTTON, self.onButton)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
        panel.SetSizer(sizer)

    # ----------------------------------------------------------------------
    def onButton(self, event):
        btn = event.GetEventObject()
        btn.Disable()
        WorkThread()
        self.dlg = ProgressDialog()
        self.dlg.ShowModal()
        btn.Enable()

app = wx.App()
frame = MainFrame(None)
frame.Show(True)
# start the applications
app.MainLoop()

1 个答案:

答案 0 :(得分:0)

你需要一个方法来阻止它 你也应该等它停下来 以下是使用您的代码的选项:

from threading import Thread
import wx
from wx.lib.pubsub import pub
import time
class WorkThread(Thread):

    def __init__(self):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.stop_work_thread = 0
        self.start()  # start the thread

    def run(self):
        for i in range(10):
            if self.stop_work_thread == 1:
                break
            time.sleep(1)
            val = 100 / 10
            wx.CallAfter(pub.sendMessage, "update", step=val)
        wx.CallAfter(pub.sendMessage, "finish")
        return

    def stop(self):
        self.stop_work_thread = 1

class ProgressDialog(wx.Dialog):
    def __init__(self,parent):
        wx.Dialog.__init__(self, parent)
        self.parent = parent
        self.abort = False
        self.progress = 0

        bSizer2 = wx.BoxSizer(wx.VERTICAL)
        self.gauge = wx.Gauge(self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL)
        self.gauge.SetValue(0)

        self.m_button1 = wx.Button(self, wx.ID_ANY, u"Stop Training", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.gauge, 0, 0, 5)
        bSizer2.Add(self.m_button1, 0, 0, 5)

        self.SetSizer(bSizer2)
        self.Layout()
        self.Centre(wx.BOTH)

        ## Connect Events

        self.m_button1.Bind(wx.EVT_BUTTON, self.on_cancel)
        pub.subscribe(self.updateProgress, "update")
        pub.subscribe(self.on_finish, "finish")

    def updateProgress(self, step):
        self.progress += step
        self.gauge.SetValue(self.progress)

    def on_cancel(self, event):
        """Cancels the conversion process"""
        self.parent.work.stop()
        self.parent.work.join()
        pub.unsubscribe(self.updateProgress, "update")
        pub.unsubscribe(self.on_finish, "finish")
        self.Destroy()
        # pub.unsubscribe(self.if_abort, 'cancel')

    def on_finish(self):
        """conversion process finished"""
        pub.unsubscribe(self.updateProgress, "update")
        pub.unsubscribe(self.on_finish, "finish")
        self.Close()

    def __del__(self):
        pass


########################################################################################

class MainFrame(wx.Frame):
    # ----------------------------------------------------------------------
    def __init__(self,parent):
        wx.Frame.__init__(self,parent)
        # Add a panel so it looks the correct on all platforms
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.btn = btn = wx.Button(self.panel, label="Start Thread")
        btn.Bind(wx.EVT_BUTTON, self.onButton)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
        self.panel.SetSizer(sizer)

    # ----------------------------------------------------------------------
    def onButton(self, event):
        btn = event.GetEventObject()
        btn.Disable()
        self.panel.work = WorkThread()
        self.dlg = ProgressDialog(self.panel)
        self.dlg.ShowModal()
        btn.Enable()

app = wx.App()
frame = MainFrame(None)
frame.Show(True)
# start the applications
app.MainLoop()