【问题标题】:Adding widgets to existing frame and show a progress bar at the same time in wxpython在 wxpython 中将小部件添加到现有框架并同时显示进度条
【发布时间】:2016-03-22 09:45:41
【问题描述】:

我有一个 wxpython 应用程序,我启动应用程序并显示主框架。它有一个按钮。当我按下按钮时,我想要一个 pyprogress (wx.lib.agw.pyprogress) 出现,并且我想在 pyprogress 运行时向主框架添加 5 个小部件。 pyprogress 应该显示如下消息:“添加小部件 1”、“添加小部件 2”...我尝试在单独的线程中运行 pyprogress,但是当我尝试更新 pyprogress 消息并添加小部件时同时到主机,我得到这个错误:

PyAssertionError: C++ assertion "wxThread::IsMain()" failed at ..\..\src\msw\evtloop.cpp(182) in wxGUIEventLoop::Dispatch(): only the main thread can process Windows messages

如何更新pyprogress 并同时将小部件添加到大型机?

更新:

以下代码可以正常工作,但我不知道它是如何工作的。它从线程更新 GUI 窗口和 PyProgress 窗口,我没有得到任何 only the main thread can process Windows messages。请解释为什么它没有给出任何错误。

import wx
import wx.lib.agw.pyprogress as PP
import threading

ID_ADD_BUTTON = 100
ID_ADD_BUTTON2 = 101

class MyForm(wx.Frame):

    def __init__(self):

        wx.Frame.__init__(self, None, wx.ID_ANY, "My Panel", size=(400,300))

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.mytext = wx.StaticText(self.panel, -1, "Start", size=(20,-1))

        self.toolbar = wx.ToolBar(self, -1, style=wx.TB_FLAT|wx.BORDER_RAISED)

        self.toolbar.AddLabelTool(ID_ADD_BUTTON, 'Refresh', wx.ArtProvider.GetBitmap(wx.ART_PLUS))

        self.toolbar.Bind(wx.EVT_TOOL, self.refreshButton, id=ID_ADD_BUTTON)

        self.toolbar.Realize()
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.toolbar, 0, wx.EXPAND)
        self.sizer.Add(self.panel, 1, wx.EXPAND)

        self.SetSizer(self.sizer)


    def refreshButton(self, event):
        self.myprogress = ProgressThread()
        self.toolbar2 = wx.ToolBar(self, -1, style=wx.TB_FLAT|wx.BORDER_RAISED)
        self.toolbar2.AddLabelTool(ID_ADD_BUTTON2, 'Print', wx.ArtProvider.GetBitmap(wx.ART_PLUS))
        self.toolbar2.Bind(wx.EVT_TOOL, self.refreshButton, id=ID_ADD_BUTTON2)
        update = MyThread(self.mytext)

        self.toolbar2.Realize()
        self.myprogress.dlg.UpdatePulse("toolbar 2 is added")
        for i in xrange(250):
            self.myprogress.dlg.UpdatePulse(str(i))
        #print "toolbar 2 is added"

        self.sizer.Add(self.toolbar2, 0, wx.EXPAND)

        self.Layout()
        for i in xrange(8000, 8005):
            self.myprogress.dlg.UpdatePulse(str(i))
            toolbar2 = wx.ToolBar(self, -1, style=wx.TB_FLAT|wx.BORDER_RAISED)
            toolbar2.AddLabelTool(ID_ADD_BUTTON2, 'Print', wx.ArtProvider.GetBitmap(wx.ART_PLUS))
            toolbar2.Bind(wx.EVT_TOOL, self.refreshButton, id=ID_ADD_BUTTON2)
            toolbar2.Realize()


            self.toolbar2.Realize()
            self.sizer.Add(toolbar2, 0, wx.EXPAND)
        self.myprogress.keepGoing = False

    def refreshButton2(self, event):
        print "button 2 is pressed"



class ProgressThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        style = wx.PD_APP_MODAL
        self.dlg = PP.PyProgress(None, -1, "Updating Data                      ",
                            "Please wait...",
                            agwStyle=style)
        self.start()

    def run(self):

        self.dlg.SetGaugeProportion(20/100.0)
        self.dlg.SetGaugeSteps(50)
        self.dlg.SetGaugeBackground(wx.WHITE)
        self.dlg.SetFirstGradientColour(wx.WHITE)
        self.dlg.SetSecondGradientColour(wx.GREEN)

        self.keepGoing = True


        while self.keepGoing:
            wx.MilliSleep(30)
            try:
                self.dlg.UpdatePulse()
            except:
                pass

        self.dlg.Destroy()


class MyThread(threading.Thread):
    def __init__(self, my_text):
        self.my_text= my_text
        threading.Thread.__init__(self)

        self.start()

    def run(self):
        for i in xrange(300):
            self.my_text.SetLabel(str(i))

# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyForm().Show()
    app.MainLoop()

【问题讨论】:

    标签: python multithreading user-interface wxpython


    【解决方案1】:

    您可以通过 wx.lib.pubsub (from wx.lib.pubsub import pub)
    实现此目的 将您的 gauge 或其他任何内容放在主循环中,然后

    pub.subscribe(self.listener, 'Update-1')
    

    例如。

    self.listener 是在您发出的线程中更新gauge 的函数

    pub.sendMessage('Update-1', data1=(a1), data2=(a2), data3=(a3))
    

    消息,每当您想用来更新进度条的事情发生时。
    侦听器函数看起来像这样:

    # Sub Pub listener
    def listener(self, data1, data2, data3):
        count = int(data1)
        size = int(data2)
        self.progress.SetRange(size)
        self.progress.SetValue(count)
        self.filehandle.SetValue(data3)
    

    UI 定义为:

    self.progress = wx.Gauge(self.panel, pos=(10,155), size=(300,10),range=100)
    self.filehandle = wx.TextCtrl(self.panel,-1, pos=(10,170),size=(300,25), value="")
    

    (此示例用于报告文件下载的进度)

    【讨论】:

    • 谢谢。单独的线程将如何发送消息以更新仪表?
    • 如答案所述,pub.sendMessage('Update-1', data1=(a1), data2=(a2), data3=(a3))
    • 但是哪个线程会将小部件添加到 GUI?主线程还是单独线程?
    • 据我所知,pubsub 不是线程安全的,因此您需要将其包装在 wx.CallAfter 中才能使用它
    【解决方案2】:

    您的所有 UI 代码都必须在主线程中。一种选择是,您将进度条代码移动到主线程中,并与执行实际工作的单独线程进行通信。我通常从单独的线程中定义自定义事件和 PostEvent。这里有一篇关于该主题的精彩文章:http://wiki.wxpython.org/LongRunningTasks

    【讨论】:

    • 请举个例子。我尝试了你的建议,但它不起作用,我仍然得到同样的错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-04
    • 2010-09-15
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多