在wxPython中使用多个线程

时间:2014-08-14 06:02:45

标签: python multithreading wxpython

我正在写一个wxpython程序。 如果您在程序中打开一个文件,它会计算文件中的行数并将其显示在staticText窗口小部件中。

以下是相关代码:

class Frame

def on_select_file(self, event):
    '''Event handler for selecting file'''
    filepath = getFile()  # This is a seperate method which asks the user to select a file and returns the path
    threading.Thread(target = methods.count_lines, args = (filepath, self.staticText)).start()

methods.py

def count_lines(filepath, staticText):
    with open(filepath) as f:
        for i, _ in enumerate(f): pass
    staticText.SetLabel(str(i+1))

我正在处理的大多数文件非常大(3-4 GB),大约有2500万行。因此,如果我打开一个大文件,需要花费几十秒来计算并显示no。的线条。但是,如果我选择一个大文件并且在staticText窗口小部件更新之前,打开另一个较小的文件,staticText窗口小部件会显示新的计数,但在一段时间后会显示上一个文件的计数。我理解这是因为之前的线程仍然在运行并在结束后更新了小部件。

我尝试将标志变量作为参数传递给计数器函数,以检查小部件是否已更新。但它似乎不起作用。还有其他方法可以避免这种情况吗?

2 个答案:

答案 0 :(得分:3)

实质上,创建一个包含当前行数作为成员的自定义事件类型,并在工作线程中使用wx.PostEvent()定期将包含静态文本小部件的类发布该类型的事件。然后,当主线程恢复并处理其事件循环时,您可以使用接收事件报告的行数来设置文本字符串。

这样的事情应该有效:

import time
from threading import *
import wx
import os.path

EVT_LINE_NUMBER_UPDATE_ID = wx.NewId()

class LineNumberUpdateEvent(wx.PyEvent):
    def __init__(self, lineNum):
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_LINE_NUMBER_UPDATE_ID)
        self.lineNumber = lineNum

class WorkerThread(Thread):
    def __init__(self, notify_window, filename):
        Thread.__init__(self)
        self._notify_window = notify_window
        self._filename = filename
        self.start()

    def run(self):
        with open(this._filename,"r") as file:
            count = 0;
            for line in file:
                 count++
                 if count % 50 == 0: # post an event every 50 lines
                     wx.PostEvent(self._notify_window, LineNumberUpdateEvent(count))
            wx.PostEvent(self._notify_window, LineNumberUpdateEvent(count)) # last event

class MainFrame(wx.Frame):
    def __init__(self, parent, id, filename):
        wx.Frame.__init__(self, parent, id, 'Threaded File Loader')
        self.status = wx.StaticText(self, -1, '', pos=(0,100))
        self.Bind(EVT_LINE_NUMBER_UPDATE_ID, self.OnLineNumberUpdate)
        if (os.path.isfile(filename))
            self.worker = WorkerThread(self,filename)

    def OnLineNumberUpdate(self, event):
        self.status.SetLabel(str(event.lineNumber))

这是根据wx Wiki上发布的一个例子改编的:

http://wiki.wxpython.org/LongRunningTasks

答案 1 :(得分:0)

我修改了 Sir Digby Chicken Caesa 的答案中的代码以满足我的需要。我没有创建自定义事件并在一段时间间隔后触发它,而是设置行计数线程,以便每次发生文件打开事件并且此线程的先前实例仍在运行时它就可以中止。

以下是实施的简短示例:

import threading.Thread

class Line_Counter(threading.Thread):
    def __init__(self, staticText, filename):
        threading.Thread.__init__(self)
        self.staticText = staticText

    def run(self):
        self.exit = False
        with open(self.filename) as f:
            if self.exit:
                return
            for count, _ in enumerate(f):   pass
        self.staticText.SetLabel(str(count + 1))

    def abort(self):
        self.exit = True


class MainFrame(wx.Frame):
    def __init__(self, parent, id, filename):
        wx.Frame.__init__(self, parent, id, 'Threaded File Loader')
        self.line_counter = False      # Set up a dummy thread variable
        #### remaining code
        self.file_open_button.Bind( wx.EVT_BUTTON, self.OnOpenFile ) # Event handler for opening file

    def OnOpenFile(self, event):
        filepath = getFile()  # Creates a file open dialog and returns the file path
        if self.line_counter: # Checks if a counter thread is already running
            self.line_counter.abort() # Stops the thread if True
        self.line_counter = Line_Counter(self.staticText, filename).start()  # Starts a new thread