Subclassing wx.Timer

Hi People,

I have a long running wx.Timer that I call DeviceMonitor 'cos that’s what it does. There are times when it’s busier than other times and so I want to run it in different modes (like ‘Operational’, ‘Startup’, ‘CloseDown’ etc etc. I end up having a whole load of if-statements and global variables, which is not ideal. When I tried to create a new class called DeviceMonitor, it seems to work (ie it doesn’t crash), but I get no events. Here it is (derived from the sample code):

import wx
import time

class TimerTest(wx.Timer):
    def __init__(self):
        wx.Timer.__init__(self)
        
    def start(self, secs):
        self.Start(secs)
        
    def stop(self):
        self.Stop()
        
class MyForm(wx.Frame):
 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Timer Tutorial 1", 
                                   size=(500,500))
 
        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        
        self.timer = TimerTest()
        self.Bind(wx.EVT_TIMER, self.update, self.timer)
        self.toggleBtn = wx.Button(panel, wx.ID_ANY, "Start")
        self.toggleBtn.Bind(wx.EVT_BUTTON, self.onToggle)
    def onToggle(self, event):
        btnLabel = self.toggleBtn.GetLabel()
        if btnLabel == "Start":
            print ("starting timer...")
            self.timer.start(1000)
            self.toggleBtn.SetLabel("Stop")
        else:
            print ("timer stopped!")
            self.timer.stop()
            self.toggleBtn.SetLabel("Start")
            
    def update(self, event):
        print ("\nupdated: "),
        print (time.ctime())
 
# Run the program
if __name__ == "__main__":
    app = wx.App()
    frame = MyForm().Show()
    app.MainLoop()

Any ideas welcome.

Kind regards,

Steve.

Try passing the frame as the owner of the timer…

class TimerTest(wx.Timer):
    def __init__(self, owner):
        wx.Timer.__init__(self, owner)

       ....
        
class MyForm(wx.Frame):
 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Timer Tutorial 1", 
                                   size=(500,500))
 
        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        
        self.timer = TimerTest(self)

        ......

[This works on Python 3.8.5 + wxPython 4.1.1 gtk3 (phoenix) wxWidgets 3.1.5 + Linux Mint 20.1]

Hi Richard,

Thanks for that. In fact that was pretty much the answer, although I don’t know what ‘owner’ is (I set it to None. Here’s the full example:

import wx
import time

class TimerTest(wx.Timer):
    def __init__(self, owner = None, id=wx.ID_ANY ):
        wx.Timer.__init__(self, owner, id )
        
    def Notify(self):
        self.Notify = self.update 
        
    def update(self, event=0):
        print ("\nupdated: "),
        print (time.ctime())
        
class MyForm(wx.Frame):
 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Timer Tutorial 1", 
                                   size=(500,500))
 
        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        
        self.timer = TimerTest()
        self.Bind(wx.EVT_TIMER, self.timer.update, self.timer)
        self.toggleBtn = wx.Button(panel, wx.ID_ANY, "Start")
        self.toggleBtn.Bind(wx.EVT_BUTTON, self.onToggle)
    def onToggle(self, event):
        btnLabel = self.toggleBtn.GetLabel()
        if btnLabel == "Start":
            print ("starting timer...")
            self.timer.Start(1000)
            self.toggleBtn.SetLabel("Stop")
        else:
            print ("timer stopped!")
            self.timer.Stop()
            self.toggleBtn.SetLabel("Start")
            
 
# Run the program
if __name__ == "__main__":
    app = wx.App()
    frame = MyForm().Show()
    app.MainLoop()

Kind regards

Steve.

method SetOwner of class wx.Timer explains the owner (if I may say so your event binding is very permissive and may get you into trouble sooner or later) :sweat_smile:

Ha Da-dada,

Thanks for that. I imagine you mean the second bind:

self.toggleBtn.Bind(wx.EVT_BUTTON, self.onToggle)

Yes it is, I just copied it from the example. You mean every button press of any button will call it, I think. I wouldn’t normally do that.

But I would like to know the difference between owner, parent and the update function in Notify. They all seem to be the same thing.

I wasn’t referring to the button, it’s the way you handle the timer event in your coding which is pretty muddled; f.i. you can take the EVT_TIMER binding statement completely out and have the same result (that would be the first of the three methods mentioned in the docu)!
It all depends on what’s the objective (with some customers that changes quicker than one can program and then it’s handy to be able doing things in different ways, but at least at the beginning there should be some tidiness :wink:)

Hi Da-Dada,

You’re right, it makes no difference. Why is that? So wx.Timer, produces an event and that needs to be routed to an event handler with .Bind, except that it doesn’t. I guess maybe .Notify or ‘owner=’ do similar things. Could you point me to something that clarifies what’s going on?

Thanks

Kind regards

Steve.

Maybe this is a bit cleaner:

import wx
import time

class TimerTest(wx.Timer):
    def __init__(self, owner = None, id=wx.ID_ANY ):
        wx.Timer.__init__(self, owner, id )
        
    def Notify(self):
        print (f"Updated: "),
        print (time.ctime())
        
class MyForm(wx.Frame):
 
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Timer Tutorial 1", size=(500,500))
 
        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        
        self.timer = TimerTest(self)
        self.toggleBtn = wx.Button(panel, wx.ID_ANY, "Start")
        self.toggleBtn.Bind(wx.EVT_BUTTON, self.onToggle)
    def onToggle(self, event):
        btnLabel = self.toggleBtn.GetLabel()
        if self.timer.IsRunning():
            print ("timer stopped!")
            self.timer.Stop()
            self.toggleBtn.SetLabel("Start")
        else:
            print ("starting timer...")
            self.timer.Start(1000)
            self.toggleBtn.SetLabel("Stop")
            
 
# Run the program
if __name__ == "__main__":
    app = wx.App()
    frame = MyForm().Show()
    app.MainLoop()

keep playing with it: the journey is the reward:hammer_and_wrench:

Timers have a couple different modes. They can be used without an owner, in which case the timer’s Notify method will be called when the timer expires. If there is an owner then an EVT_TIMER event is created and the normal event processing is done.