多线程更新 wx.Gauge 并保持当前窗口活动

26 阅读2分钟

我正在使用 python 和 wxpython 编写一个小工具,从网站下载文件。除了显示完成情况的进度条外,其他一切都正常工作。在 urlretrieve 之后,唯一移动的是进度条,GUI 变得没有响应。我知道这与线程有关,但我是真的新手,还有人能给我一些启发吗? 我的设想是在主页的框架中从网站获取搜索结果,并将结果传递给这个 DownloadListingFrame,随后动态生成按钮和静态文本。问题是,在点击下载按钮后,进度条会随着文件下载进度更新,但除此之外,整个应用程序都会挂起。在阅读了其他示例的代码后,我尝试将 doDownload 函数放在线程中并执行,它表现得好像没用线程一样...

class DownloadListingFrame ( wx.Frame ):

    data = ''

    def __init__( self, parent, result ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u'result', pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )

        self.statusbar = self.CreateStatusBar()
        self.statusbar.SetFieldsCount(3)

        self.progessBar = wx.Gauge(self.statusbar, -1, style=wx.GA_HORIZONTAL|wx.GA_SMOOTH)
        rect = self.statusbar.GetFieldRect(1)
        self.progessBar.SetPosition((rect.x+2, rect.y+2 ))
        self.progessBar.SetSize((rect.width, rect.height-4))

        buttonPos = 20
        for item in result:
            label = wx.StaticText( self, wx.ID_ANY, item, wx.Point( 120 ,buttonPos+2 ), wx.DefaultSize, 0 )
            button = wx.Button(self, id=-1,label=u'Download', pos=(20, buttonPos))
            buttonPos = buttonPos + 30
            self.Bind(wx.EVT_BUTTON, lambda x: self.Downloader(item, result[item]), button)

        self.Centre( wx.BOTH )


    def progressUpdate(self, blockCount, blockSize, totalSize):
        progressSoFar = int((float(blockCount) * float(blockSize) / float(totalSize)) * 100)
        self.progessBar.SetValue(progressSoFar)

    def doDownloade(self, realAddress, saveAsFilename):
        urllib.urlretrieve(realAddress, saveAsFilename, self.progressUpdate)

    def Downloader(self, title, url):
        saveAsPath = wx.DirDialog(self, u"save to...")
        if saveAsPath.ShowModal() == wx.ID_OK:
            realAddress = self.getRealAddress(url)
            saveAsFilename = os.path.join(saveAsPath.GetPath(), title + os.path.splitext(realAddress)[1])
            thread = threading.Thread(target=self.doDownloade(realAddress, saveAsFilename))
            thread.setDaemon(True)
            thread.start()

    def getRealAddress(self, url):
        import httplib
        siteUrl = 'www.yyets.com'
        httpConnection = httplib.HTTPConnection(siteUrl)
        httpConnection.request("GET", url)
        resp = httpConnection.getresponse()
        realAddress = resp.getheaders()[6][1]
        return realAddress

    def __del__( self ):
        pass

2、解决方案 当运行命令:

thread = threading.Thread(target=self.doDownloade(realAddress, saveAsFilename))

它会首先运行 self.doDownloade(realAddress, saveAsFilename),然后将此函数的返回值(将是 None)作为目标传递。 相反,你可以这样做:

thread = threading.Thread(target=self.doDownloade, args=(realAddress, saveAsFilename))

注意,这里我传递了函数 self.doDownloade,然后 thread 将使用在调用 thread.start 时给它的参数调用它。 顺便说一下,你也可以使用 wx.Timer 来做到这一点。通常我会觉得这是更好的工具,因为它更简单,你可以控制仪表的更新频率,从而控制用于仪表的资源量。如果你想要一个好的起点,wxPython demo 演示的仪表示例中使用了 wx.Timer。