3

我想以特定的时间间隔更改我的 GUI 中显示的文本。经过很多方法,我发现,特别是我的要求,我必须使用,time.sleep()而不是wx.Timer,但time.sleep()冻结完整的 GUI。这是我的代码示例:

import wx
import time

DWELL_TIMES = [1, 2, 1, 3]
SCREEN_STRINGS = ['nudge nudge', 'wink wink', 'I bet she does', 'say no more!']

class DM1(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        panel = wx.Panel(self)
        text_display = wx.StaticText(panel, pos = (400, 150))

        for dwell_time in DWELL_TIMES:
            text_display.SetLabel(SCREEN_STRINGS[dwell_time])
            time.sleep(float(DWELL_TIMES[dwell_time]))


app = wx.App()
DM1Frame = DM1(None, size = (800, 600))
DM1Frame.Center()
DM1Frame.Show()
app.MainLoop()

有人知道为什么会发生这种情况,以及如何使 GUI 不阻塞?我想这Threading对我有帮助,不是吗?如果是这样,将线程放入此代码中的正确方法是什么?有替代方案Threading吗?

非常感谢!

4

6 回答 6

3

正如其他人所提到的,wx.CallAfterwx.CallLater你的朋友。研究它们并学习它们。这是一个完整的工作示例,使用wx.CallLater. 我包括了其他我认为合适的重构。

import wx

DATA = [
    (1, 'nudge nudge'),
    (2, 'wink wink'),
    (1, 'I bet she does'),
    (3, 'say no more!'),
]

class Frame(wx.Frame):
    def __init__(self):
        super(Frame, self).__init__(None)
        panel = wx.Panel(self)
        self.text = wx.StaticText(panel)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddStretchSpacer(1)
        sizer.Add(self.text, 0, wx.ALIGN_CENTER)
        sizer.AddStretchSpacer(1)
        panel.SetSizer(sizer)
        self.index = 0
        self.update()
    def update(self):
        duration, label = DATA[self.index]
        self.text.SetLabel(label)
        self.index = (self.index + 1) % len(DATA)
        wx.CallLater(int(duration * 1000), self.update)

if __name__ == '__main__':
    app = wx.App(None)
    frame = Frame()
    frame.SetTitle('Example')
    frame.SetSize((400, 300))
    frame.Center()
    frame.Show()
    app.MainLoop()
于 2012-06-28T13:46:21.100 回答
2

如果您查看time.sleep()的文档,您会发现它基本上会在指定的时间间隔内阻止该线程的执行。问题是当前您的 GUI 只有一个线程,因此如果您阻塞线程,那么您会阻塞该线程中的所有执行。这意味着,正如您所经历的,GUI 在睡眠期间无法使用。

即使使用线程,time.sleep()调用也不能与 GUI 在同一个线程中,因此试图在睡眠结束后让你的 GUI 刷新将非常复杂。除此之外,它基本上是重新实现wx.Timer!重做已经为你完成的事情是没有用的。

在我看来,您的问题不应该是“我如何让睡眠发挥作用?” 以及更多“为什么不能wx.Timer正常工作?” 请详细说明您遇到的问题wx.Timer。为什么它不起作用?也许发布一些代码。我的猜测是你可能没有wx.EVT_TIMER正确绑定。看看这个教程

于 2012-06-27T02:19:33.370 回答
2

将线程放入此代码中的正确方法是什么?

尽管 usingwx.Timer是这个简化示例的正确解决方案,但如果您的真正目标是知道如何使用工作线程来执行长任务并在不冻结整个应用程序的情况下对主 GUI 进行更新,那么方法如下:

import wx
import threading
import time

class WorkerThread(threading.Thread):
    DWELL_TIMES = [1, 2, 1, 3]
    SCREEN_STRINGS = ['nudge nudge', 'wink wink', 'I bet she does', 'say no more!']

    def __init__(self, window):
        threading.Thread.__init__(self)
        self.window = window

    def run(self):
        for i in range(len(WorkerThread.DWELL_TIMES)):
            wx.CallAfter(self.window.set_text, WorkerThread.SCREEN_STRINGS[i])
            time.sleep(float(WorkerThread.DWELL_TIMES[i]))
        wx.CallAfter(self.window.close)


class DM1(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        panel = wx.Panel(self)
        self.text_display = wx.StaticText(panel, pos = (400, 150))
        self.kickoff_work()

    def kickoff_work(self):        
        t = WorkerThread(self)
        t.start()

    def set_text(self, text):
        self.text_display.SetLabel(text)

    def close(self):
        self.Close()

app = wx.App()
DM1Frame = DM1(None, size = (800, 600))
DM1Frame.Center()
DM1Frame.Show()
app.MainLoop()
于 2012-06-28T02:59:44.167 回答
0

我可能会建议你去使用wx.CallLater. 参考官方文档:http ://wxpython.org/docs/api/wx.CallLater-class.html

wx.Timer 的一个便利类,它在给定的毫秒数后调用给定的可调用对象一次,并传递任何位置或关键字参数。可调用对象的返回值在使用 GetResult 方法运行后可用。

如果您不需要获取返回值或重新启动计时器,则无需持有对该对象的引用。它会在定时器运行时持有对自身的引用(定时器有对 self.Notify 的引用)但是当定时器完成时循环将被打破,自动清理 wx.CallLater 对象。

可以在这个问题中找到可能的进一步参考:Using wx.CallLater in wxPython

于 2012-06-28T03:36:31.137 回答
0

您可以尝试创建一个全局变量来获取它首次启动的时间,然后让第二个变量获取当前时间,并查看这两个时间是否相距足够远以工作。像这样的东西:

当文本变成新的东西时,

global timestart

timestart = gettime()

然后,在您检查是否要更改代码的地方,

timestop = gettime()

if timestop - timestart >= timebetweenchanges:
    change code
于 2012-06-26T21:41:56.653 回答
0

我不明白为什么你不能为此使用计时器。它们似乎是为您需要它们的确切目的而制造的。正如 acattle 已经提到的,我写了一个关于这个主题的教程。

不过他是完全正确的。使用 time.sleep() 将冻结 GUI,因为它阻塞了 wx 的主事件循环。如果您绝对必须使用 time.sleep() (我对此表示怀疑),那么您可以使用线程。我也写了一个关于这个主题的教程。事实上,我在那个例子中实际上使用了 time.sleep() 。

于 2012-06-27T19:59:03.863 回答