循环是永远和冻结屏幕?

时间:2013-12-02 23:33:00

标签: python python-2.7

我有这个大型程序,其中一个部分包括单击按钮(此GUI在wxPython中制作)。基本上,按钮是一个计时器,当你点击它时,按钮的文字根据剩下的时间切换,例如, 5秒,4秒,3秒......但是,出于某种原因,当我点击GUI中的按钮时,整个程序冻结,我必须强制退出应用程序。 (应用程序中的其他所有工作正常并响应,直到我点击这一个按钮)。这是单击按钮时调用的事件处理程序。

提前致谢!

    def TossUpTimer(self, event):
        while self.tossuptimer > -1:
            while self.tossuptimer > 0:
                self.tossupbutton.SetLabel(str(self.tossuptimer) + "Seconds")
                time.sleep(1)
                self.tossuptimer -= 1
            if self.tossuptimer == 0:
                self.tossupbutton.SetLabel("Time is Up!")
                time.sleep(1)
                self.tossupbutton.SetLabel(str(self.tossuptimer) + "Seconds")
        self.tossuptimer = 5

1 个答案:

答案 0 :(得分:2)

Why your GUI app freezes试图解释基本思想,以及处理它的所有不同选择。

但这是一个简短的版本:

GUI应用程序的重点在于它正在运行事件循环,响应来自用户或操作系统的事件。您为特定事件编写处理程序。在处理程序返回之前,GUI无法处理下一个事件。这意味着整个应用程序都被冻结了。

您需要尽快从处理程序返回。永远不能做的是time.sleep(1)或长while循环或直接在处理程序中的任何类型。

有很多方法可以解决这个问题。最常见的两种方法是让事件循环稍后运行一些代码,或者将其移动到后台线程。 (wx增加了第三种方式,SafeYield和朋友,这在某些情况下是合适的,但可能不是你想要的。)


对于第一种选择,我们的想法是,不是循环sleep,而是编写一个处理循环的单次迭代的函数,并安排计时器来触发功能一次/秒。这被称为“将控制流向内翻”,这可能有点难以理解。

不幸的是,我并不是百分之百地确定你要做什么,因为正如Jon Clements所指出的那样,即使在顺序程序中你的逻辑也没有意义。但是我会写一些简单的东西:当你点击按钮时,它会倒计时一秒/秒,然后停止计数。

def OnTossUpTimer(self, event):
    self.tossuptimer -= 1
    if self.tossuptimer > 0:
        self.tossupbutton.SetLabel(str(self.tossuptimer) + "Seconds")
    else:
        self.tossupbutton.SetLabel("Time is Up!")
        self.tossuptimer_timer.Stop()

def TossUpTimer(self, event):
    self.tossuptimer = 5
    self.tossupbutton.SetLabel(str(self.tossuptimer) + "Seconds")
    self.tossuptimer_timer = wx.Timer(self)
    self.Bind(wx.EVT_TIMER, self.OnTossUpTimer, self.tossuptimer_timer)
    self.tossuptimer_timer.Start(1000, False)

有关详细信息,请参阅Using wx.TimersTimer文档。


线程版本在某些方面更简单,在其他方面更复杂。您可以单独保留顺序逻辑,但除了间接通过调用PostEvent之外,您无法与任何GUI小部件通信。所以:

class LabelUpdateEvent(wx.PyEvent):
    EVT_LABEL_UPDATE_ID = wx.NewId()
    def __init__(self, data):
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_LABEL_UPDATE_ID)
        self.data = data

def SetLabelOnMainThread(self, value):
    wx.PostEvent(self.SetLabelForReal, ResultEvent(value))

def SetLabelForReal(self, event):
    self.tossupbutton.SetLabel(event.data)

def TossUpTimerThread(self):
    self.tossuptimer = 5
    while self.tossuptimer > 0:
        self.SetLabelOnMainThread(str(self.tossuptimer) + "Seconds")
            time.sleep(1)
            self.tossuptimer -= 1
    self.SetLabelOnMainThread(str(self.tossuptimer) + "Time is Up!")

def TossUpTimer(self, event):
    threading.Thread(target=self.TossUpTimerThread).start()

如果你正在做很多这样的事情,你可能想要编写更通用的“on-main-thread”函数,而不是每个函数的所有这些样板文件。 (事实上​​,你可以编写一个完全通用的函数,wxPython确实应该附带,但不是。)