wxPython: frame class 在调用后加载多个命令

wxPython: frame class loads several commands AFTER it is called

我有一个函数 运行 有一些密集的命令,所以我做了一个 Spinner class,它只是一个简单的 window,它带有一个 wx.Gauge 在加载过程中脉动的小部件。

问题是,当在 Run 中调用时,window 直到初始化几秒后才出现 - self.TriangulatePoints() 实际上在 [=32= 之前完成]出现。实际上,如果我不注释掉 load.End()(关闭 window),Spinner 实例将出现并立即消失。

我认为这与线程有关,并且程序在 Spinner 启动时继续 运行。是这样吗?如果是这样,你能暂停 Run() 的进程直到 Spinner window 出现吗?

还应注意,调用 Spinner(...) 后 运行ning time.sleep(n) 在屏幕上显示的程序序列中不会改变。

def Run(self, event):

    gis.points_packed = False
    gis.triangulated = False

    load = Spinner(self, style=wx.DEFAULT_FRAME_STYLE & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX) ^ (wx.RESIZE_BORDER) & (~wx.MINIMIZE_BOX))
    load.Update('Circle packing points...')

    gis.boundary(infile=gis.loaded_boundary)

    load.Pulse()

    self.GetPoints(None, show=False)

    load.Update("Triangulating nodes...")

    self.TriangulatePoints(None, show=True)

    load.End()

########################################################

class Spinner(wx.Frame):

    def __init__(self, *args, **kwds):
        super(Spinner, self).__init__(*args, **kwds)

        self.SetSize((300,80))
        self.SetTitle('Loading')

        process = "Loading..."
        self.font = wx.Font(pointSize = 12, family = wx.DEFAULT,
                   style = wx.NORMAL, weight = wx.BOLD,
                   faceName = 'Arial')

        self.process_txt = wx.StaticText(self, -1, process)
        self.process_txt.SetFont(self.font)

        self.progress = wx.Gauge(self, id=-1, range=100, pos=(10,30), size=(280,15), name="Loading")        
        self.Update(process)

        self.Centre()
        self.Show(True)

    def End(self):
        self.Close(True)

    def Update(self,txt):

        dc = wx.ScreenDC()
        dc.SetFont(self.font)

        tsize = dc.GetTextExtent(txt)
        self.process_txt.SetPosition((300/2-tsize[0]/2,10))

        self.process_txt.SetLabel(txt)
        self.progress.Pulse()

    def Pulse(self):
        self.progress.Pulse()

您显示的代码中似乎没有任何线程,所以真的不清楚为什么您认为这与线程有任何关系。恰恰相反,事实上:AFAICS 这是由于 而不是 使用线程。你应该 运行 你的长 运行ning ("intensive") 代码在工作线程中,然后事情会工作并在 UI.

中正确显示

您不能阻塞 UI 主线程任何重要的时间,并且仍然期望 UI 正确更新。

通过在 load.Update('...') 之后立即添加 wx.Yield(),我能够解决问题。

我通过 Robin Dunn (@RobinDunn), one of the original authors of wxPython, wrote in a Google group:

post 找到了解决方案

As Micah mentioned the various yield functions are essentially a nested event loop that reads and dispatches pending events from the event queue. When the queue is empty then the yield function returns.

The reason [wx.Yield()] fixes the problem you are seeing is that your long running tasks are preventing control from returning to the main event loop and so the paint event for your custom widget will just sit in the queue until the long running task completes and control is allowed to return to the main loop. Adding the yield allows those events to be processed sooner, but you may still have problems when the long running task does finally run because any new events that need to be processed during that time (for example, the user clicks a Cancel button) will still have to wait until the LRT is finished.

Another issue to watch out for when using a yield function is that it could lead to an unexpected recursion. For example you have a LRT that periodically calls yield so events can be processed, but one of the events that happens is one whose event handler starts the LRT again.

So usually it is better to use some other way to prevent blocking of events while running a the LRT, such as breaking it up into chunks that are run from EVT_IDLE handlers, or using a thread.