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

Users of Notifier.notify(topic) must specify topic as a keyword

arg. When a Notifier listener is registered, it can be specified to run
on async.idle (to avoid threading issues).

As an aside, I have just realised that there is a naming collision
between Notifier.notify and HasProperties.notify, for anything which
sub-classes both. I don't think anything does, but this will probably
bite me in the future.
parent 9609e647
No related branches found
No related tags found
No related merge requests found
...@@ -540,8 +540,8 @@ class Image(Nifti1, notifier.Notifier): ...@@ -540,8 +540,8 @@ class Image(Nifti1, notifier.Notifier):
# if we don't yet know anything # if we don't yet know anything
# about the image data range. # about the image data range.
if drange[0] is None or drange[1] is None: if drange[0] is None or drange[1] is None:
drange = (self.header.get('cal_min', None), drange = (float(self.header['cal_min']),
self.header.get('cal_max', None)) float(self.header['cal_max']))
return drange return drange
...@@ -561,7 +561,7 @@ class Image(Nifti1, notifier.Notifier): ...@@ -561,7 +561,7 @@ class Image(Nifti1, notifier.Notifier):
Notifies any listeners of this ``Image`` (registered through the Notifies any listeners of this ``Image`` (registered through the
:class:`.Notifier` interface) on the ``'dataRange'`` topic. :class:`.Notifier` interface) on the ``'dataRange'`` topic.
""" """
self.notify('dataRange') self.notify(notifier_topic='dataRange')
def calcRange(self, sizethres=None): def calcRange(self, sizethres=None):
......
...@@ -106,7 +106,7 @@ class MelodicImage(fslimage.Image): ...@@ -106,7 +106,7 @@ class MelodicImage(fslimage.Image):
self.__tr = val self.__tr = val
if oldval != val: if oldval != val:
self.notify('tr') self.notify(notifier_topic='tr')
def getComponentTimeSeries(self, component): def getComponentTimeSeries(self, component):
......
...@@ -14,6 +14,7 @@ import inspect ...@@ -14,6 +14,7 @@ import inspect
import collections import collections
import props import props
import async
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -47,13 +48,17 @@ class Notifier(object): ...@@ -47,13 +48,17 @@ class Notifier(object):
return new return new
def register(self, name, callback, topic=None): def register(self, name, callback, topic=None, runOnIdle=False):
"""Register a listener with this ``Notifier``. """Register a listener with this ``Notifier``.
:arg name: A unique name for the listener. :arg name: A unique name for the listener.
:arg callback: The function to call - must accept this ``Notifier`` :arg callback: The function to call - must accept this ``Notifier``
instance as its sole argument. instance as its sole argument.
:arg topic: Optional topic on which fto listen for notifications. :arg topic: Optional topic on which to listen for notifications.
:arg runOnIdle: If ``True``, this listener will be called on the main
thread, via the :func:`.async.idle` function.
Otherwise this function will be called directly by the
:meth:`notify` method.
""" """
if topic is None: if topic is None:
...@@ -61,7 +66,8 @@ class Notifier(object): ...@@ -61,7 +66,8 @@ class Notifier(object):
# We use a WeakFunctionRef so we can refer to # We use a WeakFunctionRef so we can refer to
# both functions and class/instance methods # both functions and class/instance methods
self.__listeners[topic][name] = props.WeakFunctionRef(callback) self.__listeners[topic][name] = (props.WeakFunctionRef(callback),
runOnIdle)
log.debug('{}: Registered listener {} ' log.debug('{}: Registered listener {} '
'[topic: {}] (function: {})'.format( '[topic: {}] (function: {})'.format(
...@@ -88,7 +94,7 @@ class Notifier(object): ...@@ -88,7 +94,7 @@ class Notifier(object):
if listeners is None: if listeners is None:
return return
callback = listeners.pop(name, None) callback, _ = listeners.pop(name, None)
# Silently absorb invalid names - the # Silently absorb invalid names - the
# notify function may have removed gc'd # notify function may have removed gc'd
...@@ -119,12 +125,17 @@ class Notifier(object): ...@@ -119,12 +125,17 @@ class Notifier(object):
def notify(self, *args, **kwargs): def notify(self, *args, **kwargs):
"""Notify all registered listeners of this ``Notifier``. """Notify all registered listeners of this ``Notifier``.
:args notifier_topic: Must be passed as a keyword argument. The documented arguments must be passed as keyword arguments.
The topic on which to notify. Default
:args notifier_topic: The topic on which to notify. Default
listeners are always notified, regardless listeners are always notified, regardless
of the specified topic. of the specified topic.
All other arguments passed to this method are ignored. All other arguments passed to this method are ignored.
.. note:: Listeners registered with ``runOnIdle=True`` are called
via :func:`async.idle`. Other listeners are called directly.
See :meth:`register`.
""" """
topic = kwargs.get('notifier_topic', DEFAULT_TOPIC) topic = kwargs.get('notifier_topic', DEFAULT_TOPIC)
...@@ -151,7 +162,7 @@ class Notifier(object): ...@@ -151,7 +162,7 @@ class Notifier(object):
srcLine)) srcLine))
for ldict in listeners: for ldict in listeners:
for name, callback in list(ldict.items()): for name, (callback, runOnIdle) in list(ldict.items()):
callback = callback.function() callback = callback.function()
...@@ -162,5 +173,6 @@ class Notifier(object): ...@@ -162,5 +173,6 @@ class Notifier(object):
log.debug('Listener {} has been gc\'d - ' log.debug('Listener {} has been gc\'d - '
'removing from list'.format(name)) 'removing from list'.format(name))
ldict.pop(name) ldict.pop(name)
else:
callback(self) elif runOnIdle: async.idle(callback, self)
else: callback(self)
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