472,981 Members | 1,151 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,981 software developers and data experts.

wxPython - problem w/ timers

I get the following error in my app:
Expand|Select|Wrap|Line Numbers
  1. wx._core.PyAssertionError: C++ assertion "wxThread::IsMain()" failed at ..\..\sr
  2. c\common\timercmn.cpp(66) in wxTimerBase::Start(): timer can only be started fro
  3. m the main thread
The origin of the error is obvious enough but I can't think of any reason I'm not allowed to start timers from threads other than main if I so wish. Is there any going around this limitation?

TIA,
Greg
Nov 16 '07 #1
13 4228
bartonc
6,596 Expert 4TB
The only reason that I can come up with is that the docs say:
Note: A timer can only be used from the main thread.
Nov 17 '07 #2
The only reason that I can come up with is that the docs say:
Apparently there's no going around that, the timers just won't start. However, I thought about using standard Python threading timers, I wonder if they're safe to use with wxPython?
Nov 18 '07 #3
bartonc
6,596 Expert 4TB
Apparently there's no going around that, the timers just won't start. However, I thought about using standard Python threading timers, I wonder if they're safe to use with wxPython?
Ya know; I've been working on a project that needed a timer in a module that lacked a wx event handler. threading.Timer is one option (definitely thread safe), but you'll need to be careful as to how you implement the recurrence and the ability to stop it. I also looked into sched.scheduler which has nicely documented error handling. I'll whip up a threaded test to see how that might work.
Nov 18 '07 #4
bartonc
6,596 Expert 4TB
Ya know; I've been working on a project that needed a timer in a module that lacked a wx event handler. threading.Timer is one option (definitely thread safe), but you'll need to be careful as to how you implement the recurrence and the ability to stop it. I also looked into sched.scheduler which has nicely documented error handling. I'll whip up a threaded test to see how that might work.
It looks thread-safe to me (actually, forgive the slopy use threading in the BG_Timer class - I got in a rush there):
Expand|Select|Wrap|Line Numbers
  1. #-----------------------------------------------------------------------------
  2. # Name:        sched_thread_test.py
  3. # Purpose:     Test sched.Scheduler in a thread
  4. #
  5. # Author:      Barton
  6. #
  7. # Created:     2007/11/18
  8. # RCS-ID:      $Id: sched_thread_test.py $
  9. # Copyright:   (c) 2007
  10. # Licence:     Free
  11. #-----------------------------------------------------------------------------
  12. import sched, time
  13. import threading
  14.  
  15. class BG_Timer(object):
  16.     def __init__(self, interval, actionFuct, *args):
  17.         self.interval = interval
  18.         self.actionFuct = actionFuct
  19.         self.timer = sched.scheduler(time.time, time.sleep)
  20.         self.timer.enter(interval, 1, self.SimEvent, args)
  21.         self.running = False
  22.         self.runTread = threading.Thread(target=self.Run)
  23.  
  24.     def SimEvent(self, *args):
  25.         """Reschedule this handler and call the action fuction"""
  26.         startTime = time.time()
  27.         self.actionFuct(args)
  28.         interval = self.interval - (time.time() - startTime)
  29.         if self.running:
  30.             self.timer.enter(interval, 1, self.SimEvent, args)
  31.  
  32.     def Run(self):
  33.         self.running = True
  34.         self.timer.run()
  35.  
  36.     def Start(self):
  37.         self.runTread.start()
  38.  
  39.     def Stop(self):
  40.         self.running = False
  41.  
  42. def BG_Task(thrdEvent):
  43.     def periodicTask(*args):
  44.         print time.time()
  45.  
  46.     t = BG_Timer(.25, periodicTask)
  47.     print 'starting bg'
  48.     t.Start()
  49.     print 'running bg'
  50.     thrdEvent.wait()
  51.     print 'stopping bg'
  52.     t.Stop()
  53.  
  54.  
  55. threadingEvent = threading.Event()
  56. thrd = threading.Thread(target=BG_Task, args=(threadingEvent,))
  57. print 'starting thread'
  58. thrd.start()
  59. time.sleep(2)
  60. print 'stopping thread'
  61. threadingEvent.set()
  62. thrd.join()
  63.  
Nov 19 '07 #5
bartonc
6,596 Expert 4TB
It looks thread-safe to me (actually, forgive the slopy use threading in the BG_Timer class - I got in a rush there):
Here's the cleaned up version:
Expand|Select|Wrap|Line Numbers
  1. class BG_Timer(object):
  2.     def __init__(self, interval, actionFuct, *args):
  3.         self.interval = interval
  4.         self.actionFuct = actionFuct
  5.         self.timer = sched.scheduler(time.time, time.sleep)
  6.         self.timer.enter(interval, 1, self.SimEvent, args)
  7.         self.thrdEvent = threading.Event()
  8.         self.runTread = threading.Thread(target=self.Run, args=(self.thrdEvent, self.timer))
  9.  
  10.     def SimEvent(self, *args):
  11.         """Reschedule this handler and call the action fuction"""
  12.         startTime = time.time()
  13.         self.actionFuct(args)
  14.         interval = self.interval - (time.time() - startTime)
  15.         if self.thrdEvent.isSet():
  16.             self.timer.enter(interval, 1, self.SimEvent, args)
  17.  
  18.     def Run(self, thrdEvent, timer):
  19.         thrdEvent.set()
  20.         timer.run()
  21.  
  22.     def Start(self):
  23.         self.runTread.start()
  24.  
  25.     def Stop(self):
  26.         self.thrdEvent.clear()
  27.  
Nov 19 '07 #6
bartonc
6,596 Expert 4TB
Here's the cleaned up version:
Expand|Select|Wrap|Line Numbers
  1. class BG_Timer(object):
  2.     def __init__(self, interval, actionFuct, *args):
  3.         self.interval = interval
  4.         self.actionFuct = actionFuct
  5.         self.timer = sched.scheduler(time.time, time.sleep)
  6.         self.timer.enter(interval, 1, self.SimEvent, args)
  7.         self.thrdEvent = threading.Event()
  8.         self.runTread = threading.Thread(target=self.Run, args=(self.thrdEvent, self.timer))
  9.  
  10.     def SimEvent(self, *args):
  11.         """Reschedule this handler and call the action fuction"""
  12.         startTime = time.time()
  13.         self.actionFuct(args)
  14.         interval = self.interval - (time.time() - startTime)
  15.         if self.thrdEvent.isSet():
  16.             self.timer.enter(interval, 1, self.SimEvent, args)
  17.  
  18.     def Run(self, thrdEvent, timer):
  19.         thrdEvent.set()
  20.         timer.run()
  21.  
  22.     def Start(self):
  23.         self.runTread.start()
  24.  
  25.     def Stop(self):
  26.         self.thrdEvent.clear()
  27.  
While I was out cleaning the barn, I realized that I hadn't addressed the core question of "safe with wx". So I put the normal if __name__ == "__main__": guard into the original module. And as expected, everything worked great:
Expand|Select|Wrap|Line Numbers
  1. #-----------------------------------------------------------------------------
  2. # Name:        SchedThreadTestFrame.py
  3. # Purpose:     Test sched.Scheduler in a thread in wx
  4. #
  5. # Author:      <your name>
  6. #
  7. # Created:     2007/11/18
  8. # RCS-ID:      $Id: SchedThreadTestFrame.py $
  9. # Copyright:   (c) 2007
  10. # Licence:     <your licence>
  11. #-----------------------------------------------------------------------------
  12. #Boa:Frame:Frame1
  13.  
  14. import wx
  15.  
  16. import threading
  17. import time
  18.  
  19. from sched_thread_test import BG_Timer
  20.  
  21. def create(parent):
  22.     return Frame1(parent)
  23.  
  24. [wxID_FRAME1, wxID_FRAME1BUTTON1, wxID_FRAME1PANEL1, 
  25. ] = [wx.NewId() for _init_ctrls in range(3)]
  26.  
  27. class Frame1(wx.Frame):
  28.     def _init_ctrls(self, prnt):
  29.         # generated method, don't edit
  30.         wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt, pos=wx.Point(132, 132),
  31.                 size=wx.Size(400, 250), style=wx.DEFAULT_FRAME_STYLE,
  32.                 title='Sched Thread Test Frame')
  33.         self.SetClientSize(wx.Size(392, 223))
  34.         self.Bind(wx.EVT_CLOSE, self.OnFrame1Close)
  35.  
  36.         self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self, pos=wx.Point(0, 0),
  37.                 size=wx.Size(392, 223), style=wx.TAB_TRAVERSAL)
  38.  
  39.         self.button1 = wx.Button(id=wxID_FRAME1BUTTON1, label='End Task', name='button1',
  40.                 parent=self.panel1, pos=wx.Point(104, 72), size=wx.Size(144, 24), style=0)
  41.         self.button1.Bind(wx.EVT_BUTTON, self.OnButton1, id=wxID_FRAME1BUTTON1)
  42.  
  43.     def __init__(self, parent):
  44.         self._init_ctrls(parent)
  45.  
  46.         self.threadingEvent = threading.Event()
  47.         self.thrd = threading.Thread(target=self.BG_Task, args=(self.threadingEvent,))
  48.         print 'starting thread'
  49.         self.thrd.start()
  50.  
  51.  
  52.     def BG_Task(self, thrdEvent):
  53.         def periodicTask(*args):
  54.             print time.time()
  55.  
  56.         t = BG_Timer(.25, periodicTask)
  57.         print 'starting bg'
  58.         t.Start()
  59.         print 'running bg'
  60.         thrdEvent.wait()
  61.         print 'stopping bg'
  62.         t.Stop()
  63.  
  64.     def OnButton1(self, event):
  65.         print 'stopping thread'
  66.         self.threadingEvent.set()
  67.         self.thrd.join()
  68.  
  69.     def OnFrame1Close(self, event):
  70.         self.OnButton1(None)
  71.         event.Skip()
  72.  
Nov 19 '07 #7
bartonc
6,596 Expert 4TB
While I was out cleaning the barn, I realized that I hadn't addressed the core question of "safe with wx". So I put the normal if __name__ == "__main__": guard into the original module. And as expected, everything worked great:
And here's a neat trick that redirects stdout to a wxWindow. It comes in very handy for these types of text based tests:
Expand|Select|Wrap|Line Numbers
  1. #-----------------------------------------------------------------------------
  2. # Name:        wxSchedThreadTest.py
  3. # Purpose:     The wxApp
  4. #
  5. # Author:      <your name>
  6. #
  7. # Created:     2007/11/19
  8. # RCS-ID:      $Id: wxSchedThreadTest.py $
  9. # Copyright:   (c) 2007
  10. # Licence:     <your licence>
  11. #-----------------------------------------------------------------------------
  12. #!/usr/bin/env python
  13. #Boa:App:BoaApp
  14.  
  15. import wx
  16.  
  17. import SchedThreadTestFrame
  18.  
  19. modules ={u'SchedThreadTestFrame': [1,
  20.                             'Main frame of Application',
  21.                             u'SchedThreadTestFrame.py']}
  22.  
  23. class BoaApp(wx.App):
  24.     def OnInit(self):
  25.         self.main = SchedThreadTestFrame.create(None)
  26.         self.main.Show()
  27.         self.SetTopWindow(self.main)
  28.         return True
  29.  
  30. def main():
  31.     application = BoaApp(redirect=1)
  32.     application.MainLoop()
  33.  
  34. if __name__ == '__main__':
  35.     main()
  36.  
Nov 19 '07 #8
Wow... that's a lot of work you put into it, thanks very much! I've resolved most of my issues with concurrency now (also thanks to discovering functions like wx.Yield(), wx.FutureCall() etc ;)

Greg
Nov 23 '07 #9
bartonc
6,596 Expert 4TB
Wow... that's a lot of work you put into it, thanks very much! I've resolved most of my issues with concurrency now (also thanks to discovering functions like wx.Yield(), wx.FutureCall() etc ;)

Greg
wx.FutureCall() comes in very handy and I use it quite often to do initialization after the window has been created (and shown).

wx.Yield() has (for some reason) been declared deprecated:
::wxYield
bool wxYield()

Calls wxApp::Yield.

This function is kept only for backwards compatibility. Please use the wxApp::Yield method instead in any new code.
But Pythoneers rarely keep a reference to the app object, so you may want to keep an eye out for compatibility issues like this.
Nov 23 '07 #10
bartonc
6,596 Expert 4TB
Wow... that's a lot of work you put into it, thanks very much! I've resolved most of my issues with concurrency now (also thanks to discovering functions like wx.Yield(), wx.FutureCall() etc ;)

Greg
You are quite welcome. Actually, it's no trouble at all (given that my IDE - Boa Constructor - did most of the writing).

Since you were able to discover wx.FutureCall(), then you must have the wxPython in Action book. I haven't been able to find documentation for that function anywhere else. If you have a different resource, I'd really like to know about it.

Thanks.
Nov 25 '07 #11
bartonc
6,596 Expert 4TB
You are quite welcome. Actually, it's no trouble at all (given that my IDE - Boa Constructor - did most of the writing).

Since you were able to discover wx.FutureCall(), then you must have the wxPython in Action book. I haven't been able to find documentation for that function anywhere else. If you have a different resource, I'd really like to know about it.

Thanks.
This is ripped right out of the threading module in the Python lib directory:
Expand|Select|Wrap|Line Numbers
  1.  
  2. def Timer(*args, **kwargs):
  3.     return _Timer(*args, **kwargs)
  4.  
  5. class _Timer(Thread):
  6.     """Call a function after a specified number of seconds:
  7.  
  8.     t = Timer(30.0, f, args=[], kwargs={})
  9.     t.start()
  10.     t.cancel() # stop the timer's action if it's still waiting
  11.     """
  12.  
  13.     def __init__(self, interval, function, args=[], kwargs={}):
  14.         Thread.__init__(self)
  15.         self.interval = interval
  16.         self.function = function
  17.         self.args = args
  18.         self.kwargs = kwargs
  19.         self.finished = Event()
  20.  
  21.     def cancel(self):
  22.         """Stop the timer if it hasn't finished yet"""
  23.         self.finished.set()
  24.  
  25.     def run(self):
  26.         self.finished.wait(self.interval)
  27.         if not self.finished.isSet():
  28.             self.function(*self.args, **self.kwargs)
  29.         self.finished.set()
Nov 30 '07 #12
bartonc
6,596 Expert 4TB
Here's the version after I developed are real need (running wxPython in a thread).
I'm considering adding wxEvents to this soon, but for now it's a close enough approximation to drop in as a replacement for wxTimer():
Expand|Select|Wrap|Line Numbers
  1. #-----------------------------------------------------------------------------
  2. # Name:        Timer.py
  3. # Purpose:     A replacement for wxTimer that will run in a thread
  4. #
  5. # Author:      Barton Cline
  6. #
  7. # Created:     2007/11/30
  8. # RCS-ID:      $Id: Timer.py $
  9. # Copyright:   (c) 2007
  10. # Licence:     <your licence>
  11. #-----------------------------------------------------------------------------
  12.  
  13. from threading import Thread, Event
  14. import sched
  15. from time import time, sleep
  16.  
  17. class Timer(object):
  18.  
  19. ##    def __del__(self):
  20. ##        """Useless! GC won't call here while the thread is running."""
  21. ##        # So find a way to detect the main thread reaching its termination #
  22. ##        # the threading module is hooked into the python.exitfunction mechanism,
  23. ##        # so it may be posible to do too much here.
  24. ##        self.Stop()
  25.  
  26.     def __init__(self, actionFunct, *args):
  27.         # The function to perform
  28.         self.actionFunct = actionFunct
  29.         # its arguments
  30.         self.args = args
  31.  
  32.         # Handles thread termination correctly #
  33.         self.oneShot = False
  34.  
  35.         # The scheduler:
  36.         self.timer = sched.scheduler(time, sleep)
  37.         self.event = None  #store the result of sched.scheduler.enter()
  38.         # a thread-safe way to signal the scheduler to stop
  39.         self.runEnableFlag = Event() # created in the clear()ed state
  40.  
  41.         # Need a thread to activate the scheduler because sched.scheduler.run() won't return
  42.         self.runThread = Thread(target=self.Run, args=(self.runEnableFlag, self.timer))
  43.  
  44.     def SimEvent(self):
  45.         """Call the function then reschedule this handler. The scheduler will block
  46.         further attempts at calling the actionFunct if an error occurres there."""
  47.         if self.oneShot:
  48.             self.runEnableFlag.clear()
  49.         # record the ammount of time taken by
  50.         startTime = time()
  51.         # the task that gets called
  52.         self.actionFunct(*self.args)
  53.         # scaled to milliseconds
  54.         timeSpent = (time() - startTime)/1000
  55.         if timeSpent > self.interval:
  56.             # just go again after the given interval
  57.             interval = self.interval
  58.         else:
  59.             # adjust for a precise rep-rate
  60.             interval = self.interval - timeSpent
  61.         if self.runEnableFlag.isSet():
  62.             self.event = self.timer.enter(interval, 1, self.SimEvent, ())
  63.  
  64.     def Run(self, runEnableFlag, timer):
  65.         """The target of the internal thread for starting the scheduler."""
  66.         runEnableFlag.set()
  67.         timer.run()
  68.         # if sched.scheduler.run() returns, clear the flag
  69.         runEnableFlag.clear()
  70.  
  71.     def Start(self, milliseconds, oneShot=False):
  72.         """Manage resetting of the interval by cancel()ing the sched.event"""
  73.         if milliseconds <= 0:
  74.             raise ValueError, "milliseconds must be greater that zero."
  75.         # interval is scaled to milliseconds
  76.         self.interval = float(milliseconds)/1000
  77.         self.oneShot = oneShot
  78.         # Are we being re-Start()ed? #
  79.         if (not self.event is None) and (not self.timer.empty()):
  80.             # Yes, so remove this event, if it has not completed
  81.             self.timer.cancel(self.event)
  82.         # enter a 1st priority task into the scheduler's queue
  83.         self.event = self.timer.enter(self.interval, 1, self.SimEvent, ())
  84.         if not self.runEnableFlag.isSet():
  85.             self.runThread.start()
  86.  
  87.     def Stop(self):
  88.         if (self.event is not None) and (not self.timer.empty()):
  89.             # remove this event, if it has not completed
  90.             self.timer.cancel(self.event)
  91.             # and let the start threat manage the flag
  92.         else:
  93.             self.runEnableFlag.clear()
  94.  
  95.     def IsRunning(self):
  96.         return self.runThread.isAlive()
  97.  
  98.     def GetInterval(self):
  99.         """Rescale and cast the interval to int before returning."""
  100.         return int(self.interval * 1000)
  101.  
  102. def ThreadSafeTest():
  103.     # test thread safety #
  104.     def BG_Task(thrdEvent):
  105.         def periodicTask(arg1, arg2):
  106.             print arg1, arg2,
  107.             print time()
  108.  
  109.         t = Timer(periodicTask, 'hello', 'world')
  110.         print 'starting bg at ', time()
  111.         t.Start(250)
  112.         print 'running bg'
  113.         thrdEvent.wait()
  114.         print 'stopping bg'
  115.         t.Stop()
  116.  
  117.     threadingEvent = Event()
  118.     thrd = Thread(target=BG_Task, args=(threadingEvent,))
  119.     print 'starting thread'
  120.     thrd.start()
  121.     sleep(2)
  122.     print 'stopping thread'
  123.     threadingEvent.set()
  124.     thrd.join()
  125.  
  126. if __name__ == "__main__":
  127.     def periodicTask(arg1, arg2):
  128.         print arg1, arg2,
  129.         print time()
  130.  
  131.     t = Timer(periodicTask, 'hello', 'world')
  132.     print 'starting bg at ', time()
  133.     t.Start(250)
  134.     print 'running bg at 250, but interupting and resetting to 125'
  135.     sleep(.76)
  136.     t.Start(125)
  137.     print 'running bg at 125'
  138.     sleep(.5)
  139.     # passed
  140.     print 'running one-shot'
  141.     t.Start(125, True)
  142. ##    # passed
  143. ##    print 'stopping bg'
  144. ##    t.Stop()
  145. ##    # XXX FAILED!
  146. ##    t = None # will it be garbage collected?
  147. ##    # XXX FAILED!
  148. ##    del t
Nov 30 '07 #13
Thank you! I needed a way to generate timer driven events in a wxPython sub-thread and this works great!
Feb 1 '11 #14

Sign in to post your reply or Sign up for a free account.

Similar topics

8
by: Erik Johnson | last post by:
I am looking for some input on GUI libraries. I want to build a Python-driven GUI, but don't really understand the playing field very well. I have generally heard good things about wxPython. I...
0
by: Dmitry Demchuk | last post by:
Hi everybody. Recently I ran into situation with System.Threading.Timer in my ASP.NET application. I reinstalled Windows on one of my servers and got timers stop firing events after while, they...
2
by: janama | last post by:
Hi all, Using wx When adding a second timer as i have the first, the second timer adding stops the first timer (updating or stops?) . In this example im updating a uptime and localtime label....
12
by: bullockbefriending bard | last post by:
I am a complete ignoramus and newbie when it comes to designing and coding networked clients (or servers for that matter). I have a copy of Goerzen (Foundations of Python Network Programming) and...
0
by: lllomh | last post by:
Define the method first this.state = { buttonBackgroundColor: 'green', isBlinking: false, // A new status is added to identify whether the button is blinking or not } autoStart=()=>{
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 4 Oct 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
0
by: Aliciasmith | last post by:
In an age dominated by smartphones, having a mobile app for your business is no longer an option; it's a necessity. Whether you're a startup or an established enterprise, finding the right mobile app...
4
NeoPa
by: NeoPa | last post by:
Hello everyone. I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report). I know it can be done by selecting :...
1
by: Teri B | last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course. 0ne-to-many. One course many roles. Then I created a report based on the Course form and...
3
by: nia12 | last post by:
Hi there, I am very new to Access so apologies if any of this is obvious/not clear. I am creating a data collection tool for health care employees to complete. It consists of a number of...
0
NeoPa
by: NeoPa | last post by:
Introduction For this article I'll be focusing on the Report (clsReport) class. This simply handles making the calling Form invisible until all of the Reports opened by it have been closed, when it...
4
by: GKJR | last post by:
Does anyone have a recommendation to build a standalone application to replace an Access database? I have my bookkeeping software I developed in Access that I would like to make available to other...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.