0

我正在尝试编写一个 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,但我不知道将计时器绑定到事件的更好方法。有人有想法吗?

非常感谢!

4

2 回答 2

1

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

使用诸如 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”作为临时变量,因为有 Python 内置函数 id() 并且在 wx 中,小部件也有一个 id 属性:

w=Widget(..., id=int)
于 2012-06-23T19:42:56.800 回答
0

你做错了,根据这个线程,你应该将一个 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。它应该工作,但我没有测试它。

此外,正如我在这里看到的,您甚至可能不需要 id,只需将其绑定到计时器,但您可能需要直接将密钥作为参数传递。像(没有i):

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

于 2012-06-22T23:02:49.597 回答