def test_TaskThread_onError(): taskCalled = [False] onFinishCalled = [False] onErrorCalled = [False] def task(): taskCalled[0] = True raise Exception('Task error') def onFinish(): onFinishCalled[0] = True def onError(e): onErrorCalled[0] = str(e) tt = idle.TaskThread() tt.start() tt.enqueue(task, onFinish=onFinish, onError=onError) time.sleep(0.5) tt.stop() tt.join() assert taskCalled[0] assert onErrorCalled[0] == 'Task error' assert not onFinishCalled[0]
def test_TaskThread_dequeue(): called = [False] def busyTask(): time.sleep(0.5) def realTask(): called[0] = True tt = idle.TaskThread() tt.start() tt.enqueue(busyTask) tt.enqueue(realTask, taskName='realTask') time.sleep(0.25) tt.dequeue('realTask') time.sleep(0.3) tt.stop() tt.join() assert not called[0]
def test_TaskThread(): called = [False] def task(): called[0] = True tt = idle.TaskThread() tt.start() tt.enqueue(task) time.sleep(0.5) tt.stop() tt.join() assert called[0]
def test_TaskThread_onFinish(): taskCalled = [False] onFinishCalled = [False] def task(): taskCalled[0] = True def onFinish(): onFinishCalled[0] = True tt = idle.TaskThread() tt.start() tt.enqueue(task, onFinish=onFinish) time.sleep(0.5) tt.stop() tt.join() assert taskCalled[0] assert onFinishCalled[0]
def __init__(self, image, name=None, loadData=False, dataRange=None, threaded=False): """Create an ``ImageWrapper``. :arg image: A ``nibabel.Nifti1Image`` or ``nibabel.Nifti2Image``. :arg name: A name for this ``ImageWrapper``, solely used for debug log messages. :arg loadData: If ``True``, the image data is loaded into memory. Otherwise it is kept on disk (and data access is performed through the ``nibabel.Nifti1Image.dataobj`` array proxy). :arg dataRange: A tuple containing the initial ``(min, max)`` data range to use. See the :meth:`reset` method for important information about this parameter. :arg threaded: If ``True``, the data range is updated on a :class:`.TaskThread`. Otherwise (the default), the data range is updated directly on reads/writes. """ self.__image = image self.__name = name self.__taskThread = None # Save the number of 'real' dimensions, # that is the number of dimensions minus # any trailing dimensions of length 1 self.__numRealDims = len(image.shape) for d in reversed(image.shape): if d == 1: self.__numRealDims -= 1 else: break # Degenerate case - if every # dimension has length 1 if self.__numRealDims == 0: self.__numRealDims = len(image.shape) # And save the number of # 'padding' dimensions too. self.__numPadDims = len(image.shape) - self.__numRealDims # Too many shapes! Figure out # what shape we should present # the data as (e.g. at least 3 # dimensions). This is used in # __getitem__ to force the # result to have the correct # dimensionality. self.__canonicalShape = canonicalShape(image.shape) # The internal state is stored # in these attributes - they're # initialised in the reset method. self.__range = None self.__coverage = None self.__volRanges = None self.__covered = False self.reset(dataRange) if loadData: self.loadData() if threaded: self.__taskThread = idle.TaskThread() self.__taskThread.daemon = True self.__taskThread.start()
def __init__(self, name, ndims, nvals, threaded=False, settings=None, textureFormat=None, internalFormat=None, **kwargs): """Create a ``Texture``. :arg name: The name of this texture - should be unique. :arg ndims: Number of dimensions - must be 1, 2 or 3. :arg nvals: Number of values stored in each texture element. :arg threaded: If ``True``, the texture data will be prepared on a separate thread (on calls to :meth:`refresh`). If ``False``, the texture data is prepared on the calling thread, and the :meth:`refresh` call will block until it has been prepared. :arg settings: Additional settings to make available through the :class:`TextureSettingsMixin`. :arg textureFormat: Texture format to use - if not specified, this is automatically determined. If specified, an ``internalFormat`` must also be specified. :arg internalFormat: Internal texture format to use - if not specified, this is automatically determined. All other arguments are passed through to the initial call to :meth:`set`. .. note:: All subclasses must accept a ``name`` as the first parameter to their ``__init__`` method, and must pass said ``name`` through to the :meth:`__init__` method. .. note:: In normal cases, the ``textureFormat`` and ``internalFormat`` do not need to be specified - they will be automatically determined using the :func:`.data.getTextureType` function. However, there can be instances where a specific texture type needs to be used. In these instances, it is up to the calling code to ensure that the texture data can be coerced into the correct GL data type. """ TextureBase.__init__(self, name, ndims, nvals) TextureSettingsMixin.__init__(self, settings) if ((textureFormat is not None) and (internalFormat is None)) or \ ((textureFormat is None) and (internalFormat is not None)): raise ValueError('Both textureFormat and internalFormat ' 'must be specified') self.__ready = False self.__threaded = threaded # The data, type and shape are # refreshed on every call to # set or refresh (the former # calls the latter) self.__data = None self.__dtype = None self.__shape = None self.__preparedData = None # The data is refreshed on # every call to set or refresh # These attributes are set by # the __determineTextureType # and __prepareTextureData # methods (which are called # by refresh) self.__voxValXform = None self.__invVoxValXform = None self.__autoTexFmt = textureFormat is None self.__texFmt = textureFormat self.__texIntFmt = internalFormat self.__texDtype = None # If threading is enabled, texture # refreshes are performed with an # idle.TaskThread. if threaded: self.__taskThread = idle.TaskThread() self.__taskName = '{}_{}_refresh'.format( type(self).__name__, id(self)) self.__taskThread.daemon = True self.__taskThread.start() else: self.__taskThread = None self.__taskName = None self.set(**kwargs)
def __init__(self, name, nvals=1, notify=True, threaded=None, **kwargs): """Create a ``Texture3D``. :arg name: A unique name for the texture. :arg nvals: Number of values per voxel. Currently must be either ``1`` or ``3``. :arg notify: Passed to the initial call to :meth:`refresh`. :arg threaded: If ``True``, the texture data will be prepared on a separate thread (on calls to :meth:`refresh`). If ``False``, the texture data is prepared on the calling thread, and the :meth:`refresh` call will block until it has been prepared. All other keyword arguments are passed through to the :meth:`set` method, and thus used as initial texture settings. .. note:: The default value of the ``threaded`` parameter is set to the value of :attr:`.fsl.utils.platform.Platform.haveGui`. """ if nvals not in (1, 3): raise ValueError('nvals must be either 1 or 3') if threaded is None: threaded = fslplatform.haveGui texture.Texture.__init__(self, name, 3) self.__name = '{}_{}'.format(type(self).__name__, id(self)) self.__nvals = nvals self.__threaded = threaded # All of these texture settings # are updated in the set method, # called below. self.__data = None self.__preparedData = None self.__prefilter = None self.__prefilterRange = None self.__resolution = None self.__scales = None self.__interp = None self.__normalise = None self.__normaliseRange = None # These attributes are modified # in the refresh method (which is # called via the set method below). self.__ready = True # These attributes are set by the # __refresh, __determineTextureType, # and __prepareTextureData methods. self.__voxValXform = None self.__invVoxValXform = None self.__textureShape = None self.__texFmt = None self.__texIntFmt = None self.__texDtype = None # If threading is enabled, texture # refreshes are performed with an # idle.TaskThread. if threaded: self.__taskThread = idle.TaskThread() self.__taskName = '{}_{}_refresh'.format( type(self).__name__, id(self)) self.__taskThread.daemon = True self.__taskThread.start() else: self.__taskThread = None self.__taskName = None self.set(refresh=False, **kwargs) callback = kwargs.get('callback', None) self.__refresh(notify=notify, callback=callback)