wxPython长时间任务处理

Intro

总的来说就是处理掉长时间任务(LongRunningTasks),防止由wxPython绘制的GUI出现卡死状态。

这个问题我找了很久,国内论坛啥的都是千篇一律三个字“多线程”,让人一脸懵逼。我也不是那种勤奋到去买书买视频求解的好孩子,只好fαnqiαng去找了。

所以我就不得不出来解决下这个问题了。

原文地址:https://wiki.wxpython.org/LongRunningTasks

翻译

介绍

以下内容于2001年5月24日发布到comp.lang.python新闻组。由于它很好地解释了当应用程序执行长时间运行任务时保持GUI响应的选项,我决定将其逐字复制。非常感谢David Bolen花费时间和精力撰写和发表本文。--Robin

问题

Daniel Frame写道:

基本上,我已经构建了一个wxPython的GUI,当按下“START”按钮时,它运行着很长的功能。当然,GUI被锁定,直到这个功能完成。我想要在我的应用程序中添加一个“停止”按钮,如果用户已经厌倦了等待它完成,这个按钮将停止这个其他功能。 是否可以在不使用线程的情况下完成此任务? 如果是这样,我怎么可以让我的运行功能有时候检查是否按下了停止按钮? 我听说过wxyield,但我似乎无法正确实现。

David回复:

有三种可能性我会想到 - 线程, wxYield,或者在wxEVT_IDLE处理程序中进行处理。我会给出下面的一个小样本。

我喜欢用线程处理这种事(我想他们给UI留足了响应能力),但没有固定而快的铁则。所以选随意一个你觉得更舒服的。

线程真的不是那么难 - 你可以启动一个线程做你的处理,并让它在当它完成时发送一个事件到你的主要GUI线程。当它正在工作时,它可以检查事件对象或一些其他标志变量来表示它应该放弃并停止。

对于一个简单的例子,这里有一小段代码 一个简单的(无法视觉表达的)框架,并使用工作线程模拟一些处理(需要10秒,结果的值为10), 同时允许它中止。这可以推断为做任何长时间的处理并返回任何结果。 中间事件可以在处理过程中产生一些迹象表明主要的GUI线程是如何进行的 (可能更新一些大小规格或其他视觉上的效果)。

import time
from threading import *
import wx

# Button definitions
ID_START = wx.NewId()
ID_STOP = wx.NewId()

# Define notification event for thread completion
EVT_RESULT_ID = wx.NewId()

def EVT_RESULT(win, func):
    """Define Result Event."""
    win.Connect(-1, -1, EVT_RESULT_ID, func)

class ResultEvent(wx.PyEvent):
    """Simple event to carry arbitrary result data."""
    def __init__(self, data):
        """Init Result Event."""
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_RESULT_ID)
        self.data = data

# Thread class that executes processing
class WorkerThread(Thread):
    """Worker Thread Class."""
    def __init__(self, notify_window):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self._notify_window = notify_window
        self._want_abort = 0
        # This starts the thread running on creation, but you could
        # also make the GUI thread responsible for calling this
        self.start()

    def run(self):
        """Run Worker Thread."""
        # This is the code executing in the new thread. Simulation of
        # a long process (well, 10s here) as a simple loop - you will
        # need to structure your processing so that you periodically
        # peek at the abort variable
        for i in range(10):
            time.sleep(1)
            if self._want_abort:
                # Use a result of None to acknowledge the abort (of
                # course you can use whatever you'd like or even
                # a separate event type)
                wx.PostEvent(self._notify_window, ResultEvent(None))
                return
        # Here's where the result would be returned (this is an
        # example fixed result of the number 10, but it could be
        # any Python object)
        wx.PostEvent(self._notify_window, ResultEvent(10))

    def abort(self):
        """abort worker thread."""
        # Method for use by main thread to signal an abort
        self._want_abort = 1

# GUI Frame class that spins off the worker thread
class MainFrame(wx.Frame):
    """Class MainFrame."""
    def __init__(self, parent, id):
        """Create the MainFrame."""
        wx.Frame.__init__(self, parent, id, 'Thread Test')

        # Dumb sample frame with two buttons
        wx.Button(self, ID_START, 'Start', pos=(0,0))
        wx.Button(self, ID_STOP, 'Stop', pos=(0,50))
        self.status = wx.StaticText(self, -1, '', pos=(0,100))

        self.Bind(wx.EVT_BUTTON, self.OnStart, id=ID_START)
        self.Bind(wx.EVT_BUTTON, self.OnStop, id=ID_STOP)

        # Set up event handler for any worker thread results
        EVT_RESULT(self,self.OnResult)

        # And indicate we don't have a worker thread yet
        self.worker = None

    def OnStart(self, event):
        """Start Computation."""
        # Trigger the worker thread unless it's already busy
        if not self.worker:
            self.status.SetLabel('Starting computation')
            self.worker = WorkerThread(self)

    def OnStop(self, event):
        """Stop Computation."""
        # Flag the worker thread to stop if running
        if self.worker:
            self.status.SetLabel('Trying to abort computation')
            self.worker.abort()

    def OnResult(self, event):
        """Show Result status."""
        if event.data is None:
            # Thread aborted (using our convention of None return)
            self.status.SetLabel('Computation aborted')
        else:
            # Process results here
            self.status.SetLabel('Computation Result: %s' % event.data)
        # In either event, the worker is done
        self.worker = None

class MainApp(wx.App):
    """Class Main App."""
    def OnInit(self):
        """Init Main App."""
        self.frame = MainFrame(None, -1)
        self.frame.Show(True)
        self.SetTopWindow(self.frame)
        return True

if __name__ == '__main__':
    app = MainApp(0)
    app.MainLoop()

哦,如果你担心挂起在一个退出,如果你的线程由于某种原因没有终止,只需在init中添加一个self.setDaemon(1),Python就不会等待它终止。

【下面是wxYield的……我先偷个懒哈如果要翻译留言下我就有动力了23333】

你可能感兴趣的:(wxPython长时间任务处理)