Example #1
0
    def __init__(self,
                 offscreen=False,
                 other=None,
                 target=None,
                 createApp=False,
                 ready=None,
                 raiseErrors=False):
        """Create a ``GLContext``.

        :arg offscreen:   On-screen or off-screen context?

        :arg other:       Another ``GLContext`` instance with which GL state
                          should be shared.

        :arg target:      If ``other`` is not ``None``, this must be a
                          reference to a ``WXGLCanvasTarget``, the rendering
                          target for the new context.

        :arg createApp:   If ``True``, and if possible, this ``GLContext`` will
                          create and run a ``wx.App`` so that it can create a
                          ``wx.glcanvas.GLContext``.

        :arg ready:       Function which will be called when the context has
                          been created and is ready to use.

        :are raiseErrors: Defaults to ``False``. If ``True``, and if the
                          ``ready`` function raises an error, that error is not
                          caught.
        """
        def defaultReady():
            pass

        if ready is None:
            ready = defaultReady

        self.__offscreen = offscreen
        self.__ownApp = False
        self.__context = None
        self.__canvas = None
        self.__parent = None
        self.__app = None

        canHaveGui = fslplatform.canHaveGui
        haveGui = fslplatform.haveGui

        # On-screen contexts *must* be
        # created via a wx event loop
        if (not offscreen) and not (haveGui or createApp):
            raise ValueError('On-screen GL contexts must be '
                             'created on the wx.MainLoop')

        # For off-screen, only use OSMesa
        # if we have no cnoice. Otherewise
        # we use wx if possible
        if offscreen and not canHaveGui:
            self.__createOSMesaContext()
            ready()
            return

        self.__ownApp = (not haveGui) and createApp

        # A context already exists - we don't
        # need to create a GL canvas to create
        # another one.
        if other is not None:
            self.__createWXGLContext(other=other.__context, target=target)
            ready()
            return

        # Create a wx.App if we've been
        # given permission to do so
        # (via the createApp argument)
        if self.__ownApp:
            log.debug('Creating temporary wx.App')

            import fsleyes.main as fm
            self.__app = fm.FSLeyesApp()

        # Create a parent for the GL
        # canvas, and the canvas itself
        self.__createWXGLParent()
        self.__createWXGLCanvas()

        # This function creates the context
        # and does some clean-up afterwards.
        # It gets scheduled on the wx idle
        # loop.
        def create():

            app = self.__app

            self.__createWXGLContext()

            # If we've created and started
            # our own loop, kill it
            if self.__ownApp:
                log.debug('Exiting temporary wx.MainLoop')
                app.ExitMainLoop()

            if ready is not None:

                try:
                    ready()

                except Exception as e:
                    log.warning('GLContext callback function raised '
                                '{}: {}'.format(type(e).__name__, str(e)),
                                exc_info=True)
                    if raiseErrors:
                        raise e

            # Once the GL context has been
            # created, we no longer need
            # references to the wx objects
            #
            # (note: when running with macOS
            # and XQuartz over SSH/X11, a
            # GLXBadCurrentWindow can occur if
            # we close/destroy the parent/canvas
            # too soon after creating a GL
            # context. Things seem to work ok if
            # we do it after the ready() callback
            # has been called (in normal
            # circumstances, this creates the
            # FSLeyesFrame, OverlayList, and
            # DisplayContext - see fsleyes.main.main).

            # I'm not sure if this is a timing
            # issue, or if the ready() callback
            # is doing something which causes the
            # error to not occur.
            self.__parent.Close()
            self.__parent = None
            self.__canvas = None
            self.__app = None

        # If we've created our own wx.App, run its
        # main loop - we need to run the loop
        # in order to display the GL canvas and
        # context. But we can kill the loop as soon
        # as this is done (in the create function
        # above).  If an existing wx.App is running,
        # we just schedule the context creation
        # routine on it.
        idle.idle(create, alwaysQueue=True)

        if self.__ownApp:
            log.debug('Starting temporary wx.MainLoop')
            self.__app.MainLoop()
Example #2
0
    def __init__(self,
                 offscreen=False,
                 parent=None,
                 other=None,
                 target=None,
                 createApp=False,
                 ready=None):
        """Create a ``GLContext``.

        :arg offscreen: On-screen or off-screen context?

        :arg parent:    Parent ``wx`` GUI object

        :arg other:     Another ``GLContext`` instance with which GL state
                        should be shared.

        :arg target:    If ``other`` is not ``None``, this must be a reference
                        to a ``WXGLCanvasTarget``, the rendering target for the
                        new context.

        :arg createApp: If ``True``, and if possible, this ``GLContext`` will
                        create and run a ``wx.App`` so that it can create a
                        ``wx.glcanvas.GLContext``.

        :arg ready:     Function which will be called when the context has
                        been created and is ready to use.
        """

        def defaultReady():
            pass

        if ready is None:
            ready = defaultReady

        self.__offscreen = offscreen
        self.__ownApp    = False
        self.__ownParent = False
        self.__context   = None
        self.__canvas    = None
        self.__parent    = None
        self.__app       = None

        canHaveGui       = fslplatform.canHaveGui
        haveGui          = fslplatform.haveGui

        # On-screen contexts *must* be
        # created on the wx.MainLoop
        if (not offscreen) and (not haveGui):
            raise ValueError('On-screen GL contexts must be '
                             'created on the wx.MainLoop')

        # For off-screen, only use
        # OSMesa if we have no cnoice
        if offscreen and not canHaveGui:
            self.__createOSMesaContext()
            ready()

        # Use wx if possible
        else:

            self.__ownApp    = (not haveGui) and createApp
            self.__ownParent = parent is None

            if other is not None:
                self.__createWXGLContext(other=other.__context, target=target)
                return

            # Create a wx.App if we've been
            # given permission to do so
            # (via the createApp argument)
            if self.__ownApp:
                log.debug('Creating temporary wx.App')

                import fsleyes.main as fm
                self.__app = fm.FSLeyesApp()

            # Create a parent for the
            # canvas if necessary
            if self.__ownParent: self.__createWXGLParent()
            else:                self.__parent = parent

            # Create the GL canvas
            self.__createWXGLCanvas()

            # This function creates the context
            # and does some clean-up afterwards.
            # It gets scheduled on the wx idle
            # loop.
            def create():

                self.__createWXGLContext()

                if ready is not None:

                    try:
                        ready()

                    except Exception as e:
                        log.warning('GLContext callback function raised '
                                    '{}: {}'.format(type(e).__name__,
                                                    str(e)),
                                                    exc_info=True)

                # Destroying the dummy canvas
                # can be dangerous on GTK, so we
                # just permanently hide it instead.
                self.__canvas.Hide()

                # We can hide the parent as well
                # if we were the one who created
                # it.
                if self.__ownParent:
                    self.__parent.Hide()

                # If we've created and started
                # our own loop, kill it
                if self.__ownApp:
                    log.debug('Exiting temporary wx.MainLoop')
                    self.__app.ExitMainLoop()

            # If we've created our own wx.App, run its
            # main loop - we need to run the loop
            # in order to display the GL canvas and
            # context. But we can kill the loop as soon
            # as this is done (in the create function
            # above).  If an existing wx.App is running,
            # we just schedule the context creation
            # routine on it.
            idle.idle(create, alwaysQueue=True)

            if self.__ownApp:
                log.debug('Starting temporary wx.MainLoop')
                self.__app.MainLoop()
Example #3
0
def run_with_fsleyes(func, *args, **kwargs):
    """Create a ``FSLeyesFrame`` and run the given function. """

    from fsl.utils.platform import platform as fslplatform
    import fsleyes_widgets.utils.status as status

    fsleyes.configLogging()

    gc.collect()
    idle.idleLoop.reset()
    idle.idleLoop.allowErrors = True

    propagateRaise = kwargs.pop('propagateRaise', True)
    startingDelay = kwargs.pop('startingDelay', 500)
    finishingDelay = kwargs.pop('finishingDelay', 5)
    callAfterApp = kwargs.pop('callAfterApp', None)

    class State(object):
        pass

    state = State()
    state.result = None
    state.raised = None
    state.frame = None
    state.app = None
    state.dummy = None
    state.panel = None

    glver = os.environ.get('FSLEYES_TEST_GL', '2.1')
    glver = [int(v) for v in glver.split('.')]

    def init():
        fsleyes.initialise()
        props.initGUI()
        colourmaps.init()
        initialised[0] = True
        fslgl.bootstrap(glver)
        wx.CallAfter(run)

    def finish():
        state.frame.Close(askUnsaved=False, askLayout=False)
        state.dummy.Close()
        waitUntilIdle()
        realYield(100)
        state.app.ExitMainLoop()

    def run():

        overlayList = fsloverlay.OverlayList()
        displayCtx = dc.DisplayContext(overlayList)
        state.frame = fslframe.FSLeyesFrame(None, overlayList, displayCtx)

        state.app.SetOverlayListAndDisplayContext(overlayList, displayCtx)
        state.app.SetTopWindow(state.frame)

        state.frame.Show()

        try:
            if func is not None:
                state.result = func(state.frame, overlayList, displayCtx,
                                    *args, **kwargs)

        except Exception as e:
            traceback.print_exc()
            state.raised = e

        finally:
            wx.CallLater(finishingDelay, finish)

    state.app = fslmain.FSLeyesApp()
    state.dummy = wx.Frame(None)
    state.panel = wx.Panel(state.dummy)
    state.sizer = wx.BoxSizer(wx.HORIZONTAL)
    state.sizer.Add(state.panel, flag=wx.EXPAND, proportion=1)
    state.dummy.SetSizer(state.sizer)

    if callAfterApp is not None:
        callAfterApp()

    state.dummy.SetSize((100, 100))
    state.dummy.Layout()
    state.dummy.Show()

    if not initialised[0]:

        # gl already initialised
        if fslplatform.glVersion is not None:
            wx.CallLater(startingDelay, init)
        else:
            wx.CallLater(startingDelay,
                         fslgl.getGLContext,
                         ready=init,
                         raiseErrors=True)
    else:
        wx.CallLater(startingDelay, run)

    with exitMainLoopOnError(state.app) as err:
        state.app.MainLoop()

    status.setTarget(None)
    if status._clearThread is not None:
        status._clearThread.die()
        status._clearThread.clear(0.01)
        status._clearThread.join()
        status._clearThread = None

    raised = state.raised
    result = state.result

    if err[0] is not None:
        raise err[0]

    time.sleep(1)

    if raised and propagateRaise:
        raise raised

    return result
Example #4
0
def run_with_fsleyes(func, *args, **kwargs):
    """Create a ``FSLeyesFrame`` and run the given function. """

    logging.getLogger().setLevel(logging.WARNING)

    gc.collect()
    idle.idleReset()

    propagateRaise = kwargs.pop('propagateRaise', True)
    startingDelay  = kwargs.pop('startingDelay',  500)
    finishingDelay = kwargs.pop('finishingDelay', 5)
    callAfterApp   = kwargs.pop('callAfterApp',   None)

    result = [None]
    raised = [None]
    frame  = [None]
    app    = [None]

    def init():
        fsleyes.initialise()
        props.initGUI()
        colourmaps.init()
        initialised[0] = True
        fslgl.bootstrap((2, 1))
        wx.CallAfter(run)

    def finish():
        frame[0].Close(askUnsaved=False, askLayout=False)
        app[0].ExitMainLoop()

    def run():

        overlayList = fsloverlay.OverlayList()
        displayCtx  = dc.DisplayContext(overlayList)
        lockGroup   = dc.OverlayGroup(displayCtx, overlayList)

        displayCtx.overlayGroups.append(lockGroup)

        frame[0]    = fslframe.FSLeyesFrame(None,
                                            overlayList,
                                            displayCtx)

        app[0].SetOverlayListAndDisplayContext(overlayList, displayCtx)
        app[0].SetTopWindow(frame[0])

        frame[0].Show()

        try:
            if func is not None:
                result[0] = func(frame[0],
                                 overlayList,
                                 displayCtx,
                                 *args,
                                 **kwargs)

        except Exception as e:
            traceback.print_exc()
            raised[0] = e

        finally:
            wx.CallLater(finishingDelay, finish)

    app[0] = fslmain.FSLeyesApp()
    dummy  = wx.Frame(None)
    panel  = wx.Panel(dummy)
    sizer  = wx.BoxSizer(wx.HORIZONTAL)
    sizer.Add(panel, flag=wx.EXPAND, proportion=1)
    dummy.SetSizer(sizer)

    if callAfterApp is not None:
        callAfterApp()

    dummy.SetSize((100, 100))
    dummy.Layout()
    dummy.Show()

    if not initialised[0]:
        wx.CallLater(startingDelay,
                     fslgl.getGLContext,
                     parent=panel,
                     ready=init)
    else:
        wx.CallLater(startingDelay, run)

    app[0].MainLoop()
    dummy.Close()

    time.sleep(1)

    if raised[0] and propagateRaise:
        raise raised[0]

    return result[0]