【问题标题】:How to terminate thread using condition returned by wx.CallAfter()?如何使用 wx.CallAfter() 返回的条件终止线程?
【发布时间】:2017-08-15 19:26:15
【问题描述】:

我对 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()

【问题讨论】:

    标签: multithreading python-2.7 wxpython progress-bar


    【解决方案1】:

    您需要在线程中使用一个方法来停止它。
    您还应该等待它停止。
    这是使用您的代码的选项:

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

    【讨论】:

    • 非常感谢!它完美地工作!我尝试使用与 self.stop_work_thread 类似的东西作为标志来停止 WorkThread 中的 for-loop,但当时它不起作用。是不是因为我没有使用 MainFrame 作为 ProgressDialog 的父级,所以根本没有链接?
    • 可能,但看起来没有办法停止线程本身,只是进度条。 on_cancel 例程现在停止线程并等待它完成(.join 语句)。
    • 感谢您的详细回复!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-10
    • 1970-01-01
    • 2011-05-28
    • 1970-01-01
    相关资源
    最近更新 更多