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()
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()
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
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]