Skip to content
Snippets Groups Projects
Commit b323065a authored by Paul McCarthy's avatar Paul McCarthy
Browse files

I have just discovered that wx will sometimes stop generating idle

events (if user stops moving the mouse), meaning that idle tasks can get
stuck in the idle queue, and not processed until the next UI
event. Therefore, the idle loop now gets periodically called by a
wx.Timer, in addition to via EVT_IDLE events.
parent 911e7134
No related branches found
No related tags found
No related merge requests found
...@@ -163,7 +163,7 @@ def run(task, onFinish=None, onError=None, name=None): ...@@ -163,7 +163,7 @@ def run(task, onFinish=None, onError=None, name=None):
_idleRegistered = False _idleRegistered = False
"""Boolean flag indicating whether the :func:`wxIdleLoop` function has """Boolean flag indicating whether the :func:`_wxIdleLoop` function has
been registered as a ``wx.EVT_IDLE`` event handler. Checked and set been registered as a ``wx.EVT_IDLE`` event handler. Checked and set
in the :func:`idle` function. in the :func:`idle` function.
""" """
...@@ -182,6 +182,20 @@ currently queued on the idle loop (see the ``name`` parameter to the ...@@ -182,6 +182,20 @@ currently queued on the idle loop (see the ``name`` parameter to the
""" """
_idleTimer = None
"""A ``wx.Timer`` instance which is used to periodically trigger the
:func:`_wxIdleLoop` in circumstances where ``wx.EVT_IDLE`` events may not
be generated. This is created in the first call to :func:`idle`.
"""
_idleCallRate = 200
"""Minimum time (in milliseconds) between consecutive calls to
:func:`_wxIdleLoop`. If ``wx.EVT_IDLE`` events are not being fired, the
:attr:`_idleTimer` is used to maintain the idle loop at this rate.
"""
class IdleTask(object): class IdleTask(object):
"""Container object used by the :func:`idle` and :func:`_wxIdleLoop` """Container object used by the :func:`idle` and :func:`_wxIdleLoop`
functions. functions.
...@@ -204,20 +218,35 @@ class IdleTask(object): ...@@ -204,20 +218,35 @@ class IdleTask(object):
self.kwargs = kwargs self.kwargs = kwargs
def _wxIdleLoop(ev): def _wxIdleLoop(ev):
"""Function which is called on ``wx.EVT_IDLE`` events. If there """Function which is called on ``wx.EVT_IDLE`` events, and occasionally
on ``wx.EVT_TIMER` events via the :attr:`_idleTimer`. If there
is a function on the :attr:`_idleQueue`, it is popped and called. is a function on the :attr:`_idleQueue`, it is popped and called.
.. note:: The ``wx.EVT_IDLE`` event is only triggered on user interaction
(e.g. mouse movement). This means that a situation may arise
whereby a function is queued via the :func:`idle` function, but
no ``EVT_IDLE`` event gets generated. Therefore, the
:attr:`_idleTimer` object is occasionally used to call this
function as well.
""" """
import wx
global _idleQueue global _idleQueue
global _idleQueueSet global _idleQueueSet
global _idleTimer
global _idleCallRate
ev.Skip() ev.Skip()
try: try:
task = _idleQueue.get_nowait() task = _idleQueue.get_nowait()
except queue.Empty: except queue.Empty:
# Make sure that we get called periodically,
# if EVT_IDLE decides to stop firing.
_idleTimer.Start(_idleCallRate, wx.TIMER_ONE_SHOT)
return return
now = time.time() now = time.time()
...@@ -243,6 +272,8 @@ def _wxIdleLoop(ev): ...@@ -243,6 +272,8 @@ def _wxIdleLoop(ev):
if _idleQueue.qsize() > 0: if _idleQueue.qsize() > 0:
ev.RequestMore() ev.RequestMore()
else:
_idleTimer.Start(_idleCallRate, wx.TIMER_ONE_SHOT)
def inIdle(taskName): def inIdle(taskName):
...@@ -288,6 +319,7 @@ def idle(task, *args, **kwargs): ...@@ -288,6 +319,7 @@ def idle(task, *args, **kwargs):
""" """
global _idleRegistered global _idleRegistered
global _idleTimer
global _idleQueue global _idleQueue
global _idleQueueSet global _idleQueueSet
...@@ -300,9 +332,14 @@ def idle(task, *args, **kwargs): ...@@ -300,9 +332,14 @@ def idle(task, *args, **kwargs):
import wx import wx
if not _idleRegistered: if not _idleRegistered:
wx.GetApp().Bind(wx.EVT_IDLE, _wxIdleLoop) app = wx.GetApp()
app.Bind(wx.EVT_IDLE, _wxIdleLoop)
_idleTimer = wx.Timer(app)
_idleRegistered = True _idleRegistered = True
_idleTimer.Bind(wx.EVT_TIMER, _wxIdleLoop)
log.debug('Scheduling idle task ({}) on wx idle ' log.debug('Scheduling idle task ({}) on wx idle '
'loop'.format(getattr(task, '__name__', '<unknown>'))) 'loop'.format(getattr(task, '__name__', '<unknown>')))
...@@ -558,7 +595,7 @@ class TaskThread(threading.Thread): ...@@ -558,7 +595,7 @@ class TaskThread(threading.Thread):
getattr(task.func, '__name__', '<unknown>'))) getattr(task.func, '__name__', '<unknown>')))
except Exception as e: except Exception as e:
log.debug('Task crashed: {} [{}]: {}: {}'.format( log.warning('Task crashed: {} [{}]: {}: {}'.format(
task.name, task.name,
getattr(task.func, '__name__', '<unknown>'), getattr(task.func, '__name__', '<unknown>'),
type(e).__name__, type(e).__name__,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment