def test_init(): with mockCmaps() as (assetDir, sdir): fslcm.init() cmap1 = op.join(assetDir, 'colourmaps', 'cmap1.cmap') cmap2 = op.join(sdir, 'colourmaps', 'cmap2.cmap') lut1 = op.join(assetDir, 'luts', 'lut1.lut') lut2 = op.join(sdir, 'luts', 'lut2.lut') assert fslcm.getColourMaps() == ['cmap1', 'cmap2'] assert fslcm.getColourMapLabel( 'cmap1') == 'cmap1' assert fslcm.getColourMapLabel( 'cmap2') == 'cmap2' assert fslcm.getColourMapFile( 'cmap1') == cmap1 assert fslcm.getColourMapFile( 'cmap2') == cmap2 assert fslcm.getLookupTableFile('lut1') == lut1 assert fslcm.getLookupTableFile('lut2') == lut2 assert fslcm.isColourMapInstalled( 'cmap1') assert fslcm.isColourMapInstalled( 'cmap2') assert fslcm.isColourMapRegistered( 'cmap1') assert fslcm.isColourMapRegistered( 'cmap2') assert fslcm.isLookupTableInstalled( 'lut1') assert fslcm.isLookupTableInstalled( 'lut2') assert fslcm.isLookupTableRegistered('lut1') assert fslcm.isLookupTableRegistered('lut2') luts = fslcm.getLookupTables() assert len(luts) == 2 assert luts[0].key == 'lut1' assert luts[1].key == 'lut2'
def init(): fsleyes.initialise() props.initGUI() colourmaps.init() initialised[0] = True fslgl.bootstrap(glver) wx.CallAfter(run)
def fsleyes_embed(parent=None, make_fsleyesframe=True, **kwargs): """Initialise FSLeyes and create a :class:`.FSLeyesFrame`, when running within another application. .. note:: If a ``wx.App`` does not exist, one is created. :arg parent: ``wx`` parent object :arg make_fsleyesframe: bool, default is True to make a new :class:`.FSLeyesFrame` :returns: A tuple containing: - The :class:`.OverlayList` - The master :class:`.DisplayContext` - The :class:`.FSLeyesFrame` or None if make_fsleyesframe=False All other arguments are passed to :meth:`.FSLeyesFrame.__init__`. """ import fsleyes_props as props import fsleyes.gl as fslgl import fsleyes.frame as fslframe import fsleyes.overlay as fsloverlay import fsleyes.displaycontext as fsldc app = wx.GetApp() ownapp = app is None if ownapp: app = FSLeyesApp() fsleyes.initialise() colourmaps.init() props.initGUI() called = [False] ret = [None] def until(): return called[0] def ready(): frame = None fslgl.bootstrap() overlayList = fsloverlay.OverlayList() displayCtx = fsldc.DisplayContext(overlayList) if make_fsleyesframe: frame = fslframe.FSLeyesFrame(parent, overlayList, displayCtx, **kwargs) if ownapp: app.SetOverlayListAndDisplayContext(overlayList, displayCtx) # Keep a ref to prevent the app from being GC'd if make_fsleyesframe: frame._embed_app = app called[0] = True ret[0] = (overlayList, displayCtx, frame) fslgl.getGLContext(ready=ready, raiseErrors=True) idle.block(10, until=until) if ret[0] is None: raise RuntimeError('Failed to start FSLeyes') return ret[0]
def main(args=None): """Entry point for ``render``. Creates and renders an OpenGL scene, and saves it to a file, according to the specified command line arguments (which default to ``sys.argv[1:]``). """ if args is None: args = sys.argv[1:] # Initialise FSLeyes and implement hacks. # This must come first as, amongst other # things, it sets the fsleyes.assetDir. fsleyes.initialise() # Initialise colour maps module fslcm.init() # Create a GL context fslgl.getGLContext(offscreen=True, createApp=True) # Now that GL inititalisation is over, # make sure that the idle loop executes # all tasks synchronously, instead of # trying to schedule them on the wx # event loop. And make sure image textures # don't use separate threads for data # processing. with idle.idleLoop.synchronous(), \ imagetexture.ImageTexture.enableThreading(False): # Parse arguments, and # configure logging/debugging namespace = parseArgs(args) fsleyes.configLogging(namespace.verbose, namespace.noisy) # Initialise the fsleyes.gl modules fslgl.bootstrap(namespace.glversion) # Create a description of the scene overlayList, displayCtx, sceneOpts = makeDisplayContext(namespace) import matplotlib.image as mplimg # Render that scene, and save it to file bitmap, bg = render(namespace, overlayList, displayCtx, sceneOpts) if namespace.crop is not None: bitmap = autocrop(bitmap, bg, namespace.crop) mplimg.imsave(namespace.outfile, bitmap)
def main(args=None): """Entry point for ``render``. Creates and renders an OpenGL scene, and saves it to a file, according to the specified command line arguments (which default to ``sys.argv[1:]``). """ if args is None: args = sys.argv[1:] # Initialise FSLeyes and implement hacks. # This must come first as, amongst other # things, it sets the fsleyes.assetDir. fsleyes.initialise() # Initialise colour maps module fslcm.init() # Create a GL context fslgl.getGLContext(offscreen=True, createApp=True) # Parse arguments, and # configure logging/debugging namespace = parseArgs(args) fsleyes.configLogging(namespace.verbose, namespace.noisy) # Initialise the fsleyes.gl modules fslgl.bootstrap(namespace.glversion) # Create a description of the scene overlayList, displayCtx, sceneOpts = makeDisplayContext(namespace) import matplotlib.image as mplimg # Render that scene, and save it to file bitmap, bg = render(namespace, overlayList, displayCtx, sceneOpts) if namespace.crop is not None: bitmap = autocrop(bitmap, bg, namespace.crop) mplimg.imsave(namespace.outfile, bitmap)
def test_register(): cmap = tw.dedent(""" 0 0 0 0 0 1 0 1 1 1 1 1 """).strip() lut = tw.dedent(""" 1 0 0 0 label 1 2 0 0 1 label 2 3 0 1 1 label 3 4 1 1 1 label 4 """).strip() with mockCmaps() as (assetDir, sdir): fslcm.init() with open('cmap.txt', 'wt') as f: f.write(cmap) with open('lut.txt', 'wt') as f: f.write(lut) assert not fslcm.isColourMapRegistered('mycmap') fslcm.registerColourMap('cmap.txt', key='mycmap', name='My cmap') fslcm.getColourMap('mycmap') assert fslcm.isColourMapRegistered('mycmap') assert not fslcm.isColourMapInstalled( 'mycmap') assert fslcm.getColourMapLabel('mycmap') == 'My cmap' fslcm.installColourMap('mycmap') assert fslcm.isColourMapInstalled( 'mycmap') assert not fslcm.isLookupTableRegistered('mylut') fslcm.registerLookupTable('lut.txt', key='mylut', name='My lut') assert fslcm.isLookupTableRegistered('mylut') assert not fslcm.isLookupTableInstalled( 'mylut') assert fslcm.getLookupTable('mylut').name == 'My lut' fslcm.installLookupTable('mylut') assert fslcm.isLookupTableInstalled( 'mylut')
def main(args=None): """Entry point for ``render``. Creates and renders an OpenGL scene, and saves it to a file, according to the specified command line arguments (which default to ``sys.argv[1:]``). """ if args is None: args = sys.argv[1:] # Create a GL context fslgl.getGLContext(offscreen=True, createApp=True) # Initialise FSLeyes and implement hacks fsleyes.initialise() fsleyesmain.hacksAndWorkarounds() # Initialise colour maps module fslcm.init() # Parse arguments, and # configure logging/debugging namespace = parseArgs(args) fsleyes.configLogging(namespace) # Initialise the fsleyes.gl modules fslgl.bootstrap(namespace.glversion) # Create a description of the scene overlayList, displayCtx, sceneOpts = makeDisplayContext(namespace) import matplotlib.image as mplimg # Render that scene, and save it to file bitmap = render(namespace, overlayList, displayCtx, sceneOpts) mplimg.imsave(namespace.outfile, bitmap)
def embed(parent, callback=None, **kwargs): """Initialise FSLeyes and create a :class:`.FSLeyesFrame`, when running within another application. :arg parent: ``wx`` parent object :arg callback: A function which will be called when FSLeyes is ready. Must accept three positional arguments: - The :class:`.OverlayList` - The master :class:`.DisplayContext` - The :class:`.FSLeyesFrame` All other arguments are passed to :meth:`.FSLeyesFrame.__init__`. """ import fsleyes_props as props import fsleyes.gl as fslgl import fsleyes.frame as fslframe import fsleyes.overlay as fsloverlay import fsleyes.displaycontext as fsldc fsleyes.initialise() colourmaps.init() props.initGUI() def ready(): fslgl.bootstrap() overlayList = fsloverlay.OverlayList() displayCtx = fsldc.DisplayContext(overlayList) frame = fslframe.FSLeyesFrame( parent, overlayList, displayCtx, **kwargs) if callback is not None: callback(overlayList, displayCtx, frame) fslgl.getGLContext(parent=parent, ready=ready)
def embed(mkFrame=True, **kwargs): """Initialise FSLeyes and create a :class:`.FSLeyesFrame`, when running within another application. .. note:: In most cases, this function must be called from the ``wx.MainLoop``. :arg mkFrame: Defaults to ``True``. If ``False``, FSLeyes is initialised, but a :class:`.FSLeyesFrame` is not created. If you set this to ``False``, you must ensure that a ``wx.App`` object exists before calling this function. :returns: A tuple containing: - The :class:`.OverlayList` - The master :class:`.DisplayContext` - The :class:`.FSLeyesFrame` (or ``None``, if ``makeFrame is False``). All other arguments are passed to :meth:`.FSLeyesFrame.__init__`. """ import fsleyes_props as props import fsleyes.gl as fslgl import fsleyes.frame as fslframe import fsleyes.overlay as fsloverlay import fsleyes.displaycontext as fsldc # initialise must be called before # a FSLeyesApp gets created, as it # tries to access app_icon.png fsleyes.initialise() app = wx.GetApp() ownapp = app is None if ownapp and (mkFrame is False): raise RuntimeError('If mkFrame is False, you ' 'must create a wx.App before ' 'calling fsleyes.main.embed') if ownapp: app = FSLeyesApp() colourmaps.init() props.initGUI() called = [False] ret = [None] def until(): return called[0] def ready(): fslgl.bootstrap() overlayList = fsloverlay.OverlayList() displayCtx = fsldc.DisplayContext(overlayList) if mkFrame: frame = fslframe.FSLeyesFrame(None, overlayList, displayCtx, **kwargs) else: frame = None if ownapp: app.SetOverlayListAndDisplayContext(overlayList, displayCtx) # Keep a ref to prevent the app from being GC'd frame._embed_app = app called[0] = True ret[0] = (overlayList, displayCtx, frame) fslgl.getGLContext(ready=ready, raiseErrors=True) idle.block(10, until=until) if ret[0] is None: raise RuntimeError('Failed to start FSLeyes') return ret[0]
def main(args=None): """*FSLeyes* entry point. Shows a :class:`.FSLeyesSplash` screen, parses command line arguments, and shows a :class:`.FSLeyesFrame`. Returns an exit code. """ if args is None: args = sys.argv[1:] # Hack to allow render to # be called via fsleyes.main if len(args) >= 1 and args[0] == 'render': import fsleyes.render as render render.main(args[1:]) sys.exit(0) # the fsleyes.initialise function figures # out the path to asset files (e.g. cmaps) fsleyes.initialise() # Hook which allows us to run a jupyter # notebook server from a frozen version # of FSLeyes if len(args) >= 1 and args[0] == 'notebook': from fsleyes.actions.notebook import nbmain fsleyes.configLogging() sys.exit(nbmain(args)) # initialise colour maps - this must be # done before parsing arguments, as if # the user asks for help, available # colourmaps/luts will be listed. colourmaps.init() # Function to bootstrap the GUI - keep # reading below. def initgui(): # First thing's first. Create a wx.App, # and initialise the FSLeyes package. app = FSLeyesApp() # Create a splash screen frame splash = fslsplash.FSLeyesSplash(None) return app, splash # If it looks like the user is asking for # help, or using cliserver to pass arguments # to an existing FSLeyes instance, then we # parse command line arguments before # creating a wx.App and showing the splash # screen. This means that FSLeyes help/ # version information can be retrieved # without a display, and hopefully fairly # quickly. # # Otherwise we create the app and splash # screen first, so the splash screen gets # shown as soon as possible. Arguments # will get parsed in the init function below. # # The argparse.Namespace object is kept in a # list so it can be shared between the sub- # functions below # # If argument parsing bombs out, we put the # exit code here and return it at the bottom. namespace = [None] exitCode = [0] # user asking for help - parse args first if (len(args) > 0) and (args[0] in ('-V', '-h', '-fh', '-cs', '--version', '--help', '--fullhelp', '--cliserver')): namespace = [parseArgs(args)] app, splash = initgui() # otherwise parse arguments on wx.MainLoop # below else: app, splash = initgui() # We are going do all processing on the # wx.MainLoop, so the GUI can be shown # as soon as possible, and because it is # difficult to force immediate GUI # refreshes when not running on the main # loop - this is important for FSLeyes, # which displays status updates to the # user while it is loading overlays and # setting up the interface. # # All of the work is defined in a series # of functions, which are chained together # via ugly callbacks, but which are # ultimately scheduled and executed on the # wx main loop. def init(splash): # See FSLeyesSplash.Show # for horribleness. splash.Show() # Parse command line arguments if necessary. # If arguments are invalid, the parseargs # module will raise SystemExit. try: if namespace[0] is None: errmsg = strings.messages['main.parseArgs.error'] errtitle = strings.titles['main.parseArgs.error'] with status.reportIfError(errtitle, errmsg, raiseError=True): namespace[0] = parseArgs(args) # But the wx.App.MainLoop eats SystemExit # exceptions for unknown reasons, and # causes the application to exit # immediately. This makes testing FSLeyes # (e.g. code coverage) impossible. So I'm # catching SystemExit here, and then # telling the wx.App to exit gracefully. except (SystemExit, Exception) as e: app.ExitMainLoop() exitCode[0] = getattr(e, 'code', 1) return # Configure logging (this has to be done # after cli arguments have been parsed, # but before initialise is called). fsleyes.configLogging(namespace[0].verbose, namespace[0].noisy) # Initialise sub-modules/packages. The # buildGui function is passed through # as a callback, which gets called when # initialisation is complete. initialise(splash, namespace[0], buildGui) def buildGui(): # Now the main stuff - create the overlay # list and the master display context, # and then create the FSLeyesFrame. overlayList, displayCtx = makeDisplayContext(namespace[0], splash) app.SetOverlayListAndDisplayContext(overlayList, displayCtx) frame = makeFrame(namespace[0], displayCtx, overlayList, splash) app.SetTopWindow(frame) frame.Show() # Check that $FSLDIR is set, complain # to the user if it isn't if not namespace[0].skipfslcheck: wx.CallAfter(fslDirWarning, frame) # Check for updates. Ignore point # releases, otherwise users might # get swamped with update notifications. if namespace[0].updatecheck: import fsleyes.actions.updatecheck as updatecheck wx.CallAfter(updatecheck.UpdateCheckAction(), showUpToDateMessage=False, showErrorMessage=False, ignorePoint=False) # start notebook server if namespace[0].notebookFile is not None: namespace[0].notebook = True namespace[0].notebookFile = op.abspath(namespace[0].notebookFile) if namespace[0].notebook: from fsleyes.actions.notebook import NotebookAction frame.menuActions[NotebookAction](namespace[0].notebookFile) # start CLI server if namespace[0].cliserver: cliserver.runserver(overlayList, displayCtx) # Shut down cleanly on sigint/sigterm. # We do this so that any functions # registered with atexit will actually # get called. nsignals = [0] def sigHandler(signo, frame): log.debug('Signal received - FSLeyes is shutting down...') # first signal - try to exit cleanly if nsignals[0] == 0: nsignals[0] += 1 exitCode[0] = signo # kill any modal windows # that are open for mdlg in app.modals: mdlg.EndModal(wx.ID_CANCEL) wx.CallAfter(app.ExitMainLoop) # subsequent signals - exit immediately else: sys.exit(signo) signal.signal(signal.SIGINT, sigHandler) signal.signal(signal.SIGTERM, sigHandler) # Note: If no wx.Frame is created, the # wx.MainLoop call will exit immediately, # even if we have scheduled something via # wx.CallAfter. In this case, we have # already created the splash screen, so # all is well. wx.CallAfter(init, splash) # under mac, use appnope to make sure # we don't get put to sleep. This is # primarily for the jupyter notebook # integration - if the user is working # with a notebook in the web browser, # macos might put FSLeyes to sleep, # causing the kernel to become # unresponsive. try: import appnope appnope.nope() except ImportError: pass app.MainLoop() shutdown() return exitCode[0]
def initialise(splash, namespace, callback): """Called by :func:`main`. Bootstraps/Initialises various parts of *FSLeyes*. The ``callback`` function is asynchronously called when the initialisation is complete. :arg splash: The :class:`.FSLeyesSplash` screen. :arg namespace: The ``argparse.Namespace`` object containing parsed command line arguments. :arg callback: Function which is called when initialisation is done. """ import fsl.utils.settings as fslsettings import fsleyes_props as props import fsleyes.gl as fslgl import fsleyes.colourmaps as colourmaps props.initGUI() colourmaps.init() # The save/load directory defaults # to the current working directory. curDir = op.normpath(os.getcwd()) # But if we are running as a frozen application, check to # see if FSLeyes has been started by the system (e.g. # double-clicking instead of being called from the CLI). # # If so, we set the save/load directory # to the user's home directory instead. if fslplatform.frozen: fsleyesDir = op.dirname(__file__) # If we're a frozen OSX application, # we need to adjust the FSLeyes dir # (which will be: # [install_dir]/FSLeyes.app/Contents/MacOS/fsleyes/), # # Because the cwd will default to: # [install_dir/ if fslplatform.os == 'Darwin': fsleyesDir = op.normpath( op.join(fsleyesDir, '..', '..', '..', '..')) # Similar adjustment for linux elif fslplatform.os == 'Linux': fsleyesDir = op.normpath(op.join(fsleyesDir, '..')) if curDir == fsleyesDir: curDir = op.expanduser('~') fslsettings.write('loadSaveOverlayDir', curDir) # Initialise silly things if namespace.bumMode: import fsleyes.controls.orthotoolbar as ot import fsleyes.controls.lightboxtoolbar as lbt ot.BUM_MODE = True lbt.BUM_MODE = True # This is called by fsleyes.gl.getGLContext # when the GL context is ready to be used. def realCallback(): fslgl.bootstrap(namespace.glversion) callback() try: # Force the creation of a wx.glcanvas.GLContext object, # and initialise OpenGL version-specific module loads. # The splash screen is used as the parent of the dummy # canvas created by the gl.getWXGLContext function. fslgl.getGLContext(parent=splash, ready=realCallback) except: log.error('Unable to initialise OpenGL!', exc_info=True) splash.Destroy() sys.exit(1)