绑定for循环中创建的计时器事件的最佳方法

时间:2012-06-22 21:56:31

标签: python events timer wxpython bind

我正在尝试编写一个python代码,该代码在for循环中创建并初始化 n 计时器,并将它们绑定到某些事件。下面的代码是我正在做的方式的一个例子:

import wx

#dm1 = {a dewell times values dictionary}
#screens = [a list of dm1 keys]
trials = range(1, 3)
timers = range(0, 4)


class DM1(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        panel = wx.Panel(self)

        self.Go = wx.Button(panel, label = 'Go!', pos = (600, 450))
        self.Bind(wx.EVT_BUTTON, self.BuildTimers, self.Go)

    def BuildTimers(self, event):
        self.timers = {} 
        cum_timer = 0

        for trial in trials:
            for timer in timers:
                key = (timer, trial)
                new_timer = wx.Timer(self)
                cum_timer += dm1[screens[timer]]
                new_timer.Start(cum_timer * 1000, False)

                new_timer.mykey = key 
                self.timers[key] = new_timer

        self.Bind(wx.EVT_TIMER, self.Screens)

    def Screens(self, event):
        if event.GetEventObject() == self.timers[event.GetEventObject().mykey]:
            print event.GetEventObject().mykey
            if event.GetEventObject().mykey[0] == 0:
                print 'You bastard!'

            if event.GetEventObject().mykey[0] == 1:
                print 'you vicious...'                

            if event.GetEventObject().mykey[0] == 2:
                print 'heartless bastard!'

            if event.GetEventObject().mykey[0] == 3:
                print 'Oh It makes me mad!'

app = wx.App()
frame = DM1(None, title = 'IG', size = (wx.DisplaySize()))
frame.Show()
app.MainLoop()

定时器在我指定的时间没有开始:trial的第二个循环似乎覆盖了第一个循环。例如,完整代码中的语句print event.GetEventObject().mykey打印

(0,1)(1,1)(1,2)(2,1)(3,1)(0,2)(3,1)(2,1)

而不是

(0,1)(1,1)(2,1)(3,1)(0,2)(1,2)(2,2)(3,2)

我想问题出在GetEventObject,但我不知道将计时器绑定到事件的更好方法。有人有想法吗?

非常感谢!

2 个答案:

答案 0 :(得分:1)

使用wxPython时,我发现让widgets使用默认参数生成自己的id更明智。然后,您只需询问小部件的小部件ID。这是一个危险的做法,意味着调试非常棘手,使用手动魔术数字id值并将小部件的id值发送到该值,特别是对于4000以下的ID,因为在低范围内定义了一堆。例如,一些内部方法将要求“给我一个id为id的小部件”,而不是获取一些内部默认小部件,该方法将取代你的计时器小部件。

使用预定义内容(如TIMER_ID = 4000或甚至更难以捉摸的错误TIMER_ID = 4000 + someoffset),为代码添加额外的维护成本。

我发现最灵活,最可靠的方法是使用id = -1或id = wx.NewId()。然后说

ident = myTimer.GetId()
self.timers[ ident ] = myTimer

self.timers[ myTimer ] = myTimer

self.timers.append( myTimer ) 

维护订单,索引就像您在代码中使用ID字段一样。

另一个好的做法是避免使用“id”作为临时变量,因为在wx中有Python内置函数id()AND,小部件也有一个id属性:

w=Widget(..., id=int)

答案 1 :(得分:0)

你做错了,根据this thread,你应该将id参数传递给计时器。对于你的情况,这是我能想到的:

def BuildTimers(self, event):
    self.timers = {} 
    cum_timer = 0

    i = 0
    for trial in trials:
        for timer in timers:
            i += 1
            key = (timer, trial)
            new_timer = wx.Timer(self, id=i)
            cum_timer += dm1[screens[timer]]
            new_timer.Start(cum_timer * 1000, False)

            self.timers[i] = key

            self.Bind(wx.EVT_TIMER, lambda event, i=i: self.Screens(event, the_id=i), id=i)

def Screens(self, event, the_id=None):
    print "In the timer id =", the_id, "which is", self.timers[the_id]

为什么你做的不起作用

您正在设置Timer实例的属性,然后在您的函数中使用GetEventObject()。显然,Timer不是一个事件对象......你这样做的方式,所有的计时器都会调用相同的函数而不知道哪一个正在调用它。所以我传递了一个唯一的参数id,我想这需要一个int。为此,我使用了lambda。它应该工作,但我没有测试它。

另外,正如我看到here,您甚至可能不需要ID,只需将其绑定到计时器,但您可能需要直接将该键作为参数传递。类似的东西(没有i):

self.Bind(wx.EVT_TIMER, lambda event, k=key: self.Screens(event, the_key=k), new_timer)