Пример #1
0
def getGLContext(**kwargs):
    """Create and return a GL context object for on- or off-screen OpenGL
    rendering.

    If a context object has already been created, it is returned.
    Otherwise, one is created and returned.

    See the :class:`GLContext` class for details on the arguments.

    .. warning:: Use the ``ready`` argument to
                 :meth:`GLContext.__init__`, and don't call :func:`bootstrap`
                 until it has been called!
    """

    import sys

    thismod = sys.modules[__name__]

    # A context has already been created
    if hasattr(thismod, '_glContext'):

        # If a callback was provided,
        # make sure it gets called.
        callback = kwargs.pop('ready', None)
        if callback is not None:
            idle.idle(callback)

        return thismod._glContext

    thismod._glContext = GLContext(**kwargs)

    return thismod._glContext
Пример #2
0
def _test_screenshot(panel, overlayList, displayCtx, stype, imgfile):

    import matplotlib.image as mplimg
    import fsleyes.actions.screenshot as screenshot
    import fsleyes.views.orthopanel as orthopanel

    if isinstance(panel, orthopanel.OrthoPanel):
        panel.sceneOpts.showCursor = False
        panel.sceneOpts.showLabels = False

    img = fslimage.Image(op.join(datadir, imgfile))

    overlayList.append(img)

    with tempdir():

        fname = 'test_screenshot_{}.png'.format(stype)

        realYield(100)
        idle.idle(screenshot.screenshot, panel, fname)
        realYield()

        bfname = op.join(datadir, 'test_screenshot_{}.png'.format(stype))
        screenshot = mplimg.imread(fname)
        benchmark = mplimg.imread(bfname)

        result, diff = compare_images(screenshot, benchmark, 50)

        print('Comparing {} with {}: {}'.format(fname, bfname, diff))

        assert result
Пример #3
0
    def __volumeChanged(self, *a):
        """Called when the :attr:`.NiftiOpts.volume` property changes. Selects
        the corresponding row in the :class:`.WidgetGrid`.
        """

        # Only change the row if we are
        # currently visible, otherwise
        # this will screw up the focus.
        if not self.IsShown():
            return

        grid = self.__grid
        opts = self.displayCtx.getOpts(self.__overlay)

        log.debug('Overlay volume changed ({}) - updating '
                  'selected component'.format(opts.volume))

        # The setOverlay method updates the grid size on
        # the idle loop when the selected overlay changes,
        # so we have to update the selection on idle too,
        # otherwise the following sequence of events:
        #
        #  1. Overlay change (asynchronously schedules 3)
        #  2. Volume change (directly calls SetSelection on
        #     wrongly-sized grid)
        #  3. Grid refresh
        #
        # may raise an error
        idle.idle(grid.SetSelection, opts.volume, -1)
Пример #4
0
    def _navModeChar(self, ev, canvas, key):
        """Handles key presses in ``nav`` mode.

        Arrow key presses in ``nav`` mode update the
        :attr:`.DisplayContext.location`.  Arrow keys map to the
        horizontal/vertical axes, and -/+ keys map to the depth axis of the
        canvas which was the target of the event.
        """

        if len(self.overlayList) == 0:
            return False

        try:
            ch = chr(key)
        except Exception:
            ch = None

        dirs = [0, 0, 0]
        copts = canvas.opts

        if key == wx.WXK_LEFT: dirs[copts.xax] = -1
        elif key == wx.WXK_RIGHT: dirs[copts.xax] = 1
        elif key == wx.WXK_UP: dirs[copts.yax] = 1
        elif key == wx.WXK_DOWN: dirs[copts.yax] = -1
        elif ch in ('+', '='): dirs[copts.zax] = 1
        elif ch in ('-', '_'): dirs[copts.zax] = -1
        else: return False

        def update():
            self.displayCtx.location.xyz = self.__offsetLocation(*dirs)

        # See comment in _zoomModeMouseWheel about timeout
        idle.idle(update, timeout=0.1)

        return True
Пример #5
0
    def _zoomModeMouseWheel(self,
                            ev,
                            canvas,
                            wheel,
                            mousePos=None,
                            canvasPos=None):
        """Handles mouse wheel events in ``zoom`` mode.

        Zooms in/out of the canvas by updating the :attr:`.SceneOpts.zoom`
        property.
        """

        if wheel > 0: wheel = 50
        elif wheel < 0: wheel = -50
        else: return False

        opts = self.viewPanel.sceneOpts

        # see comment in OrthoViewProfile._zoomModeMouseWheel
        # about timeout
        def update():
            opts.zoom += wheel

        idle.idle(update, timeout=0.1)

        return True
Пример #6
0
        def addLabel(startIdx):

            # If the user closes this panel while the
            # label list is being created, wx will
            # complain when we try to append things
            # to a widget that has been destroyed.
            try:

                for labelIdx in range(startIdx,
                                      min(startIdx + blockSize, nlabels)):

                    # A new request to re-create the list has
                    # been made - cancel this creation chain.
                    if self.__labelListCreateKey != myCreateKey:
                        return

                    label = lut[labelIdx]
                    widget = LabelWidget(self, lut, label)

                    self.__labelList.Append(str(label.value),
                                            clientData=label.value,
                                            extraWidget=widget)

                if labelIdx == nlabels - 1:
                    status.update('Lookup table label list created.')
                    self.Enable()
                    self.__labelList.Enable()
                else:
                    idle.idle(addLabel, labelIdx + 1)

            except RuntimeError:
                pass
Пример #7
0
    def __textureUpdateLoop(self):
        """This method is called via the :func:`.idle.idle` function.
        It loops through all :class:`.RenderTexture` instances, and
        refreshes any that have been marked as *dirty*.

        Each call to this method causes one ``RenderTexture`` to be
        refreshed. After a ``RenderTexture`` has been refreshed, if there
        are dirty more ``RenderTexture`` instances, this method re-schedules
        itself to be called again via :func:`.idle.idle`.
        """

        if len(self.__updateQueue) == 0 or len(self.__textures) == 0:
            return

        idx = self.__updateQueue.pop(0)

        if self.__textureDirty[idx]:

            tex = self.__textures[idx]

            log.debug('Refreshing texture slice {} (zax {})'.format(
                idx, self.__zax))

            self.__refreshTexture(tex, idx)

        if len(self.__updateQueue) > 0:
            idle.idle(self.__textureUpdateLoop)
Пример #8
0
    def __init__(self, globj):
        """Create a ``RenderTextureStack``. An update listener is registered
        on the ``GLObject``, so that the textures can be refreshed whenever it
        changes.

        :arg globj: The :class:`.GLObject` instance.
        """

        self.name = '{}_{}_{}'.format(
            type(self).__name__,
            type(globj).__name__, id(self))

        self.__globj              = globj
        self.__maxNumTextures     = 256
        self.__maxWidth           = 1024
        self.__maxHeight          = 1024
        self.__defaultNumTextures = 64
        self.__defaultWidth       = 256
        self.__defaultHeight      = 256

        self.__textureDirty       = []
        self.__textures           = []

        self.__lastDrawnTexture   = None
        self.__updateQueue        = []

        idle.idle(self.__textureUpdateLoop)

        log.debug('{}.init ({})'.format(type(self).__name__, id(self)))
Пример #9
0
def test_idle():

    called = [False]

    def task(arg, kwarg1=None):
        called[0] = arg == 1 and kwarg1 == 2

    def errtask(arg, kwarg1=None):
        raise Exception('Task which was supposed to crash crashed!')

    assert idle.idleLoop.callRate > 0

    # Run directly
    _run_without_wx(idle.idle, task, 1, kwarg1=2, name='direct')
    assert called[0]

    called[0] = False

    # Run on wx idle loop
    _run_with_wx(idle.idle, task, 1, kwarg1=2)
    assert called[0]

    # Run a crashing task directly
    with pytest.raises(Exception):
        idle.idle(errtask, 1, kwarg1=2)

    # Run a crashing task on idle loop - error should not propagate
    _run_with_wx(idle.idle, errtask, 1, kwarg1=2)
Пример #10
0
    def __refreshAllTextures(self, *a):
        """Marks all :class:`.RenderTexture`  instances as *dirty*, so that
        they will be refreshed by the :meth:`.__textureUpdateLoop`.
        """

        if self.__lastDrawnTexture is not None:
            lastIdx = self.__lastDrawnTexture
        else:
            lastIdx = len(self.__textures) // 2

        aboveIdxs = list(range(lastIdx, len(self.__textures)))
        belowIdxs = list(range(lastIdx - 1, -1, -1))

        idxs = [0] * len(self.__textures)

        for i in range(len(self.__textures)):

            if len(aboveIdxs) > 0 and len(belowIdxs) > 0:
                if i % 2: idxs[i] = belowIdxs.pop(0)
                else:     idxs[i] = aboveIdxs.pop(0)

            elif len(aboveIdxs) > 0: idxs[i] = aboveIdxs.pop(0)
            else:                    idxs[i] = belowIdxs.pop(0)

        self.__textureDirty = [True] * len(self.__textures)
        self.__updateQueue  = idxs

        idle.idle(self.__textureUpdateLoop)
Пример #11
0
        def calcCorr():

            correlations = self.calculateCorrelation(xyz, data)

            # The correlation overlay is updated/
            # created on the main thread.
            def update():

                try:

                    # A correlation overlay already
                    # exists for the source overlay
                    # - update its data
                    if corrOvl is not None:
                        corrOvl[:] = correlations

                    # The correlation overlay hasn't
                    # been created yet - create a
                    # new overlay with the correlation
                    # values.
                    else:
                        self.__createCorrelateOverlay(ovl, correlations)

                finally:
                    fslstatus.clearStatus()
                    self.__correlateFlag.clear()

            idle.idle(update)
Пример #12
0
    def _zoomModeMouseWheel(self,
                            ev,
                            canvas,
                            wheel,
                            mousePos=None,
                            canvasPos=None):
        """Handles mouse wheel events in ``zoom`` mode.

        Mouse wheel motion in zoom mode increases/decreases the zoom level
        of the target canvas.
        """
        if wheel > 0: wheel = 50
        elif wheel < 0: wheel = -50

        copts = canvas.opts
        minzoom = copts.getAttribute('zoom', 'minval')
        maxzoom = copts.getAttribute('zoom', 'maxval')

        # Over SSH/X11, mouse wheel events seem to get queued,
        # and continue to get processed after the user has
        # stopped spinning the mouse wheel, which is super
        # frustrating. So we do the update asynchronously, and
        # set a time out to drop the event, and prevent the
        # horribleness from happening.
        def update():
            newZoom = np.clip(copts.zoom + wheel, minzoom, maxzoom)
            copts.zoom = newZoom

        idle.idle(update, timeout=0.1)

        return True
Пример #13
0
    def _viewModeMouseWheel(self,
                            ev,
                            canvas,
                            wheel,
                            mousePos=None,
                            canvasPos=None):
        """Handles mouse wheel events in ``view`` mode.

        Updates the :attr:.LightBoxCanvasOpts.topRow` property, thus scrolling
        through the slices displayed on the canvas.
        """

        # When we scroll up, we move to lower slices,
        # so a positive scroll direction corresponds
        # to negative slice direction, and vice versa.
        if wheel > 0: wheel = -1
        elif wheel < 0: wheel = 1
        else: return False

        opts = self.__canvas.opts

        # See comment in OrthoViewProfile._zoomModeMouseWheel
        # about timeout
        def update():
            opts.topRow += wheel

        idle.idle(update, timeout=0.1)

        return True
Пример #14
0
    def _sliceModeMouseWheel(self,
                             ev,
                             canvas,
                             wheel,
                             mousePos=None,
                             canvasPos=None):
        """Handles mouse wheel movement in ``slice`` mode.

        Mouse wheel movement on a canvas changes the depth location displayed
        on that canvas.
        """

        if len(self.overlayList) == 0:
            return False

        dirs = [0, 0, 0]
        copts = canvas.opts

        if wheel > 0: dirs[copts.zax] = 1
        elif wheel < 0: dirs[copts.zax] = -1

        pos = self.__offsetLocation(*dirs)

        def update():
            self.displayCtx.location[copts.zax] = pos[copts.zax]

        # See comment in _zoomModeMouseWheel about timeout
        idle.idle(update, timeout=0.1)

        return True
Пример #15
0
 def __eventloop(self):
     """Event loop used for the IPython kernel. Calls :meth:`__kernelDispatch`,
     then schedules a future call to ``__eventloop`` via :func:`.idle.idle`
     loop.
     """
     self.__kernelDispatch()
     idle.idle(self.__eventloop, after=self.__kernel._poll_interval)
Пример #16
0
def test_idle_alwaysQueue4():

    # Test scheduling the task when
    # wx is not present - the task
    # should just be executed immediately
    called = [False]

    def task():
        called[0] = True

    import fsl.utils.platform
    with mock.patch.dict('sys.modules', {'wx': None}):

        # idle uses the platform module to
        # determine whether a GUI is available,
        # so we have to reload it
        reload_module(fsl.utils.platform)
        idle.idle(task, alwaysQueue=True)

        with pytest.raises(ImportError):
            import wx

    reload_module(fsl.utils.platform)

    assert called[0]
Пример #17
0
def _test_synchronous():

    called = [False]

    def task():
        called[0] = True

    def test_async():
        called[0] = False
        idle.idle(task)
        assert not called[0]
        _wait_for_idle_loop_to_clear()
        assert called[0]

    oldval = idle.idleLoop.neverQueue

    try:
        idle.idleLoop.neverQueue = False
        test_async()

        with idle.idleLoop.synchronous():
            called[0] = False
            idle.idle(task)
            assert called[0]

        test_async()

    finally:
        idle.idleLoop.neverQueue = oldval
Пример #18
0
    def setOverlay(self, overlay, volLabels, refreshGrid=True):
        """Sets the :class:`.Image` to display component labels for.
        The :class:`.WidgetGrid` is re-populated to display the
        component-label mappings contained in the
        :class:`.VolumeLabels` instance associated with the overlay.

        :arg refreshGrid: If ``True`` (the default), the ``WidgetGrid``
                          displaying component labels is refreshed. This
                          flag is used internally (see
                          :meth:`__overlayTypeChanged`).
        """

        self.__deregisterCurrentOverlay()
        self.__grid.ClearGrid()

        if not (isinstance(overlay, fslimage.Image) and
                len(overlay.shape) == 4):
            self.__grid.Refresh()
            return

        log.debug('Registering new overlay: {}'.format(overlay))

        self.__overlay   = overlay
        self.__volLabels = volLabels
        display          = self.displayCtx.getDisplay(overlay)
        opts             = display.opts

        volLabels.register(             self.name, self.__labelsChanged)
        opts     .addListener('volume', self.name, self.__volumeChanged)
        display  .addListener('overlayType',
                              self.name,
                              self.__overlayTypeChanged)

        # We refresh the component grid on idle, in
        # case multiple calls to setOverlay are made
        # in quick succession - only the most recent
        # request will be executed.
        def doRefreshGrid():

            # The overlay might have been cleared
            # by the time this function gets called
            if self.__overlay is None:
                self.__grid.Refresh()
                return

            ncomps = self.__volLabels.numComponents()

            self.__grid.SetGridSize(ncomps, 2, growCols=[1])

            self.__grid.SetColLabel(0, strings.labels[self, 'componentColumn'])
            self.__grid.SetColLabel(1, strings.labels[self, 'labelColumn'])

            self.__recreateTags()
            self.__volumeChanged()

        if refreshGrid:
            idle.idle(doRefreshGrid,
                      name='{}_doRefreshGrid'.format(self.name),
                      skipIfQueued=True)
Пример #19
0
    def queuetask():

        print('Queuetask running')

        idle.idle(task1, after=0.01, name=name)
        idle.idle(task2, after=0.01, name=name, dropIfQueued=True)

        print('Queuetask finished')
Пример #20
0
    def notify(self, *args, **kwargs):
        """Notify all registered listeners of this ``Notifier``.

        The documented arguments must be passed as keyword arguments.

        :arg topic: The topic on which to notify. Default
                    listeners are always notified, regardless
                    of the specified topic.

        :arg value: A value passed through to the registered listener
                    functions. If not provided, listeners will be passed
                    a value of ``None``.

        All other arguments passed to this method are ignored.

        .. note:: Listeners registered with ``runOnIdle=True`` are called
                  via :func:`idle.idle`. Other listeners are called directly.
                  See :meth:`register`.
        """

        topic     = kwargs.get('topic', None)
        value     = kwargs.get('value', None)
        listeners = self.__getListeners(topic)

        if len(listeners) == 0:
            return

        if log.getEffectiveLevel() <= logging.DEBUG:
            stack   = inspect.stack()
            frame   = stack[1]
            srcMod  = '...{}'.format(frame[1][-20:])
            srcLine = frame[2]

            log.debug('{}: Notifying {} listeners (topic: {}) [{}:{}]'.format(
                type(self).__name__,
                len(listeners),
                topic,
                srcMod,
                srcLine))

        for listener in listeners:

            callback = listener.callback
            name     = listener.name

            # The callback, or the owner of the
            # callback function may have been
            # gc'd - remove it if this is the case.
            if callback is None:
                log.debug('Listener {} has been gc\'d - '
                          'removing from list'.format(name))
                self.__listeners[listener.topic].pop(name)

            elif not listener.enabled:
                continue

            elif listener.runOnIdle: idle.idle(callback, self, topic, value)
            else:                    callback(           self, topic, value)
Пример #21
0
    def __onCancel(self, ev=None):
        """Called when the *Cancel* button is pushed. Resets the
        :attr:`.NiftiOpts.displayXform` attribute of the overlay being
        transformed, and then calls
        :meth:`.OrthoPanel.toggleEditTransformPanel` to close this panel.
        """

        self.__resetAllOverlays()
        idle.idle(self.__ortho.toggleEditTransformPanel)
Пример #22
0
    def updateColourBarTexture(self, *a):
        """Called whenever the colour bar texture needs to be updated. """
        def update():
            self.__genColourBarTexture()
            self.Refresh()

        name = '{}_updateColourBarTexture'.format(id(self))

        idle.idle(update, name=name, skipIfQueued=True)
Пример #23
0
def waitUntilIdle():

    called = [False]
    def flag():
        called[0] = True

    idle.idle(flag)

    while not called[0]:
        realYield(50)
Пример #24
0
    def __destroyTextures(self):
        """Destroys all :class:`.RenderTexture` instances. This is performed
        asynchronously, via the ``idle.idle`` function.
        """

        texes = self.__textures
        self.__textures = []

        for tex in texes:
            idle.idle(tex.destroy)
Пример #25
0
    def setOverlay(self, overlay, volLabels):
        """Set the :class:`.Image` shown on this ``LabelGrid``. A listener is
        registered with its :class:`.VolumeLabels`, and its component-label
        mappings displayed on the :class:`.WidgetGrid`.
        """

        self.__deregisterCurrentOverlay()
        self.__grid.ClearGrid()
        self.__labelTags.clear()

        if not (isinstance(overlay, fslimage.Image)
                and len(overlay.shape) == 4):
            self.__grid.Refresh()
            return

        log.debug('Registering new overlay: {}'.format(overlay))

        self.__overlay = overlay
        self.__volLabels = volLabels

        volLabels.register(self.name, self.__labelsChanged)

        # We refresh the label grid on idle, in
        # case multiple calls to setOverlay are
        # made in quick succession - only the most
        # recent request will be executed.
        def createGrid():

            # The grid is initialised with length 0.
            # Rows for each label are are added in
            # the __createTags method, which is called
            # here, and as needed when the LUT or
            # MelodicClassification for the currently
            # selected overlay change.
            self.__grid.SetGridSize(0, 2, [1])
            self.__grid.ShowRowLabels(False)
            self.__grid.ShowColLabels(True)
            self.__grid.SetColLabel(0, strings.labels[self, 'labelColumn'])
            self.__grid.SetColLabel(1, strings.labels[self, 'componentColumn'])

            # The overlay might have been cleared
            # by the time this function gets called
            if self.__overlay is None:
                self.__grid.Refresh()
                return

            self.__createTags()
            self.refreshTags()
            self.__grid.Refresh()

        idle.idle(createGrid,
                  name='{}_createGrid'.format(self.name),
                  skipIfQueued=True)
Пример #26
0
    def __kernelDispatch(self):
        """Event loop used for the IPython kernel. Submits the kernel function
        to the :func:`.idle.idle` loop, and schedules another call to this
        method on the ``zmq`` event loop.

        This means that, while the ``zmq`` loop runs in its own thread, the
        IPython kernel is executed on the main thread.
        """

        idle.idle(self.__kernel.do_one_iteration)
        self.__ioloop.call_later(self.__kernel._poll_interval,
                                 self.__kernelDispatch)
Пример #27
0
    def __init__(self, parent, overlayList, displayCtx, frame, msg,
                 warnCondition, changeTo):
        """Create a ``DisplaySpaceWarning``.

        :arg parent:        ``wx`` parent object
        :arg overlayList:   The :class:`.OverlayList`
        :arg displayCtx:    The :class:`.DisplayContext`
        :arg frame:         The :class:`.FSLeyesFrame`

        :arg msg:           Message to display

        :arg warnCondition: One of ``'world'``, ``'overlay'``, or
                            ``'not overlay'``

        :arg changeTo:      One of ``'world'`` or ``'overlay'``
        """

        fslpanel.FSLeyesPanel.__init__(self, parent, overlayList, displayCtx,
                                       frame)

        self.__warnCondition = warnCondition
        self.__changeTo = changeTo
        self.__dsWarning = wx.StaticText(self)
        self.__changeDS = wx.Button(self)

        self.__dsWarning.SetLabel(msg)
        self.__changeDS.SetLabel(strings.labels[self, 'changeDS'])

        self.__dsWarning.SetForegroundColour((192, 0, 0, 255))

        self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.__realSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.__realSizer.Add((1, 1), flag=wx.EXPAND)
        self.__realSizer.Add(self.__dsWarning)
        self.__realSizer.Add((10, 1))
        self.__realSizer.Add(self.__changeDS, flag=wx.ALIGN_CENTRE_VERTICAL)
        self.__realSizer.Add((1, 1), flag=wx.EXPAND)
        self.__sizer.Add(self.__realSizer, flag=wx.EXPAND)

        self.SetSizer(self.__sizer)

        self.__changeDS.Bind(wx.EVT_BUTTON, self.__onChangeDS)

        displayCtx.addListener('displaySpace', self.name,
                               self.__displaySpaceChanged)
        displayCtx.addListener('selectedOverlay', self.name,
                               self.__displaySpaceChanged)
        overlayList.addListener('overlays', self.name,
                                self.__displaySpaceChanged)

        idle.idle(self.__displaySpaceChanged)
Пример #28
0
    def __onPaint(self, ev):
        """Called on ``wx.EVT_PAINT`` events. Schedules :meth:`Refresh`
        to be called on the idle loop.
        """
        def doRefresh():
            if fwidgets.isalive(self):
                self.Refresh()

        # GL canvases do need to be refreshed
        # on EVT_PAINT events. If they are not,
        # the canvas will be corrupted.
        if not self.__freezeDraw:
            idle.idle(doRefresh)
Пример #29
0
def _wait_for_idle_loop_to_clear():

    if fslplatform.haveGui:
        import wx
        idleDone = [False]

        def busywait():
            idleDone[0] = True

        idle.idle(busywait)

        while not idleDone[0]:
            wx.GetApp().Yield()
Пример #30
0
    def __onCancel(self, ev=None):
        """Called when the Cancel button is pushed. Calls
        :meth:`.OrthoPanel.toggleCropMode` - this will result in
        this ``CropImagePanel`` being destroyed.

        This method is also called programmatically from the :meth:`__onCrop`
        method after the image is cropped.
        """

        # Do asynchronously, because we don't want
        # this CropImagePanel being destroyed from
        # its own event handler.
        idle.idle(self.__ortho.toggleCropMode)