【问题标题】:wxPython thread that does not block the GUI?wxPython 线程不会阻塞 GUI?
【发布时间】:2014-05-24 11:56:24
【问题描述】:

我是 wxPython 和线程的新手。我正在尝试将一些代码移动到一个线程中,以便它在执行时不再阻塞我的 GUI,并且我可以按下另一个按钮来终止线程。但是,完成此操作后,代码仍然会阻止我的 GUI。如何编写此代码以使我的线程不会阻塞 GUI?

import wx, sys
import threading
import time


class mywxframe(wx.Frame):

    global sizer2, WorkerThread

    def __init__(self):
        wx.Frame.__init__(self, None)
        pnl = wx.Panel(self)
        szr = wx.BoxSizer(wx.VERTICAL)
        pnl.SetSizer(szr)
        szr2 = sizer2(self, pnl)
        szr.Add(szr2, 1, wx.ALL | wx.EXPAND, 10)
        log = wx.TextCtrl(pnl, -1,style=wx.TE_MULTILINE, size=(300,-1))
        szr.Add(log, 1, wx.ALL, 10)
        btn3 = wx.Button(pnl, -1, "Stop")
        btn3.Bind(wx.EVT_BUTTON, self.OnStop)

        szr.Add(btn3, 0, wx.ALL, 10)

        redir = RedirectText(log)
        sys.stdout=redir

        szr.Fit(self)
        self.Show()


    def sizer2(self, panel):
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        btn2 = wx.Button(panel, -1, "OK",)
        self.Bind(wx.EVT_BUTTON, self.OnStart, btn2)
        sizer.Add(btn2, 0, wx.ALL, 10)
        return sizer


    def WorkerThread(self):    
        self.dead = False
        while (not self.dead):
            for i in range(0,10):
                print "printing", i
                time.sleep(3)

    def OnStart(self, event):
         our_thread = threading.Thread(target=WorkerThread(self))
         our_thread.start()

    def OnStop(self, event):
        self.dead = True


class RedirectText(object):
    def __init__(self, aWxTextCtrl):
        self.out=aWxTextCtrl

    def write(self, string):
        self.out.WriteText(string)




app = wx.PySimpleApp()
frm = mywxframe()
app.MainLoop()

【问题讨论】:

  • 请提供一个小的可运行示例。此外,您确实想从线程内部调用任何小部件的方法。如果需要与 wx 交互,请使用线程安全的方法,如 wx.CallAfter 或 wx.PostEvent

标签: multithreading user-interface wxpython blocking


【解决方案1】:

你有几个问题。

首先,如果您要将sizer2WorkerThread 定义为方法,您应该将它们用作方法,而不是全局变量。

接下来,当您创建线程时,您调用WorkerThread 并将其返回值(无)传递给线程。这是你阻止 GUI 的地方。

    def OnStart(self, event):
        our_thread = threading.Thread(target=WorkerThread(self))
        our_thread.start()

相反,您应该将可调用对象(WorkerThread 不带 ())传递给线程,以便它能够在新线程的上下文中调用它。

最后,由于self.out.WriteText 操作一个UI 对象,它必须在GUI 线程的上下文中调用。使用wx.CallAfter 是一种简单的方法。

以下是您的示例更新了这些更改:

import wx, sys
import threading
import time

print wx.version()

class mywxframe(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None)
        pnl = wx.Panel(self)
        szr = wx.BoxSizer(wx.VERTICAL)
        pnl.SetSizer(szr)
        szr2 = self.sizer2(pnl)
        szr.Add(szr2, 1, wx.ALL | wx.EXPAND, 10)
        log = wx.TextCtrl(pnl, -1,style=wx.TE_MULTILINE, size=(300,-1))
        szr.Add(log, 1, wx.ALL, 10)
        btn3 = wx.Button(pnl, -1, "Stop")
        btn3.Bind(wx.EVT_BUTTON, self.OnStop)

        szr.Add(btn3, 0, wx.ALL, 10)

        redir = RedirectText(log)
        sys.stdout=redir

        szr.Fit(self)
        self.Show()


    def sizer2(self, panel):
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        btn2 = wx.Button(panel, -1, "OK",)
        self.Bind(wx.EVT_BUTTON, self.OnStart, btn2)
        sizer.Add(btn2, 0, wx.ALL, 10)
        return sizer


    def WorkerThread(self):    
        self.dead = False
        while (not self.dead):
            for i in range(0,10):
                print "printing", i
                if self.dead:
                    break
                time.sleep(3)
        print 'thread exiting'

    def OnStart(self, event):
        our_thread = threading.Thread(target=self.WorkerThread)
        our_thread.start()

    def OnStop(self, event):
        self.dead = True


class RedirectText(object):
    def __init__(self, aWxTextCtrl):
        self.out=aWxTextCtrl

    def write(self, string):
        wx.CallAfter(self.out.WriteText, string)


app = wx.App()
frm = mywxframe()
app.MainLoop()

【讨论】:

    猜你喜欢
    • 2012-06-11
    • 2015-12-04
    • 2012-10-03
    • 2021-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多