10 KiB
wxPython:如何从线程更新进度条
原文:https://www.blog.pythonlibrary.org/2013/09/04/wxpython-how-to-update-a-progress-bar-from-a-thread/
不时地,我看到有人想知道如何创建一个进度条并更新它。所以我决定编写一个更新进度条的示例应用程序(技术上是一个 wx。仪表部件)。在本教程中,我们将创建一个带有按钮的框架。当按钮被按下时,它将启动一个包含进度条的对话框,并启动一个线程。该线程是一个伪线程,因为它除了在 20 秒内每秒向对话框发送一次更新之外,不做任何特别的事情。然后对话被破坏。我们来看看吧!
import time
import wx
from threading import Thread
from wx.lib.pubsub import Publisher
########################################################################
class TestThread(Thread):
"""Test Worker Thread Class."""
#----------------------------------------------------------------------
def __init__(self):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.start() # start the thread
#----------------------------------------------------------------------
def run(self):
"""Run Worker Thread."""
# This is the code executing in the new thread.
for i in range(20):
time.sleep(1)
wx.CallAfter(Publisher().sendMessage, "update", "")
########################################################################
class MyProgressDialog(wx.Dialog):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Dialog.__init__(self, None, title="Progress")
self.count = 0
self.progress = wx.Gauge(self, range=20)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.progress, 0, wx.EXPAND)
self.SetSizer(sizer)
# create a pubsub listener
Publisher().subscribe(self.updateProgress, "update")
#----------------------------------------------------------------------
def updateProgress(self, msg):
"""
Update the progress bar
"""
self.count += 1
if self.count >= 20:
self.Destroy()
self.progress.SetValue(self.count)
########################################################################
class MyFrame(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, title="Progress Bar Tutorial")
# 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):
"""
Runs the thread
"""
btn = event.GetEventObject()
btn.Disable()
TestThread()
dlg = MyProgressDialog()
dlg.ShowModal()
btn.Enable()
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.MainLoop()
让我们花几分钟来分析一下。我们从底层开始。首先运行的是 MyFrame 类。当您运行这个脚本时,您应该会看到类似这样的内容:
如您所见,这些代码只是创建了一个简单的框架,上面有一个按钮。如果您按下按钮,将创建以下对话框,并启动一个新线程:
让我们来看看构成对话框的那部分代码:
########################################################################
class MyProgressDialog(wx.Dialog):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Dialog.__init__(self, None, title="Progress")
self.count = 0
self.progress = wx.Gauge(self, range=20)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.progress, 0, wx.EXPAND)
self.SetSizer(sizer)
# create a pubsub listener
Publisher().subscribe(self.updateProgress, "update")
#----------------------------------------------------------------------
def updateProgress(self, msg):
"""
Update the progress bar
"""
self.count += 1
if self.count >= 20:
self.Destroy()
self.progress.SetValue(self.count)
这段代码创建了一个带有 wx 的对话框。仪表小部件。标尺是进度条后面的实际小部件。无论如何,我们在对话框的 init 的最后创建了一个 pubsub 监听器。这个侦听器接受将触发 updateProgress 方法的消息。我们将看到消息在线程类中被发送。在 updateProgress 方法中,我们递增计数器并更新 wx。通过设置其值来测量。我们还检查计数是否大于或等于 20,这是仪表的范围。如果是的话,我们就毁掉这个对话。
现在,我们可以开始查看线程代码了:
########################################################################
class TestThread(Thread):
"""Test Worker Thread Class."""
#----------------------------------------------------------------------
def __init__(self):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.start() # start the thread
#----------------------------------------------------------------------
def run(self):
"""Run Worker Thread."""
# This is the code executing in the new thread.
for i in range(20):
time.sleep(1)
wx.CallAfter(Publisher().sendMessage, "update", "")
这里我们创建一个线程并立即启动它。该线程在 20 的范围内循环,并在每次迭代中使用时间模块休眠一秒钟。每次睡眠后,它向对话框发送一条消息,告诉它更新进度条。
更新 wxPython 2.9 的代码
上一节中的代码是使用 pubsub 的旧 API 编写的,随着 wxPython 2.9 的出现,它已经被抛弃了。所以如果你试图在 2.9 中运行上面的代码,你可能会遇到问题。因此,为了完整起见,下面是使用新的 pubsub API 的代码版本:
import time
import wx
from threading import Thread
from wx.lib.pubsub import pub
########################################################################
class TestThread(Thread):
"""Test Worker Thread Class."""
#----------------------------------------------------------------------
def __init__(self):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.start() # start the thread
#----------------------------------------------------------------------
def run(self):
"""Run Worker Thread."""
# This is the code executing in the new thread.
for i in range(20):
time.sleep(1)
wx.CallAfter(pub.sendMessage, "update", msg="")
########################################################################
class MyProgressDialog(wx.Dialog):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Dialog.__init__(self, None, title="Progress")
self.count = 0
self.progress = wx.Gauge(self, range=20)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.progress, 0, wx.EXPAND)
self.SetSizer(sizer)
# create a pubsub receiver
pub.subscribe(self.updateProgress, "update")
#----------------------------------------------------------------------
def updateProgress(self, msg):
""""""
self.count += 1
if self.count >= 20:
self.Destroy()
self.progress.SetValue(self.count)
########################################################################
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
# 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):
"""
Runs the thread
"""
btn = event.GetEventObject()
btn.Disable()
TestThread()
dlg = MyProgressDialog()
dlg.ShowModal()
btn.Enable()
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()
注意,现在您导入的是 pub 模块,而不是 Publisher 模块。还要注意,你必须使用关键字参数。更多信息参见公共订阅文档。
包扎
此时,您应该知道如何创建自己的进度对话框,并从线程中更新它。您可以使用此代码的变体来创建文件下载程序。如果你这样做了,你将需要检查你正在下载的文件的大小,然后分块下载,这样你就可以创建 wx 了。使用适当的范围进行测量,并在下载每个块时更新它。我希望这能给你一些如何在你自己的项目中使用这个小部件的想法。
附加阅读
- wxPython 和线程
- wxPython wiki: 长时间运行的任务

