From 08053ee4677b76ac9742f939b5f4be33d70d0769 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Mon, 29 Feb 2016 14:05:48 +0000 Subject: [PATCH] Import problem fixed in actions/about.py (was crashing for non-WX render tool). Re-worked fsl GUI tool intiailisation so that arguments are parsed on the wx.MainLoop - trying to get the FSLeyes splash screen shown ASAP. --- fsl/__init__.py | 188 +++++++++++++++++++---------------- fsl/fsleyes/actions/about.py | 5 +- fsl/tools/fsleyes.py | 25 +++-- 3 files changed, 120 insertions(+), 98 deletions(-) diff --git a/fsl/__init__.py b/fsl/__init__.py index 3e9127b1b..7ac6455d0 100644 --- a/fsl/__init__.py +++ b/fsl/__init__.py @@ -82,10 +82,6 @@ def main(args=None): :arg args: Command line arguments. If not provided, ``sys.argv`` is used. """ - # Search the environment for FSLDIR - fsldir = os.environ.get('FSLDIR', None) - fslEnvActive = fsldir is not None - if args is None: args = sys.argv[1:] @@ -94,88 +90,118 @@ def main(args=None): allTools = _getFSLToolNames() fslTool, namespace, toolArgv = _parseTopLevelArgs(args, allTools) - # If this is a GUI tool, create - # the wx application before calling - # fslTool.init(), in case it does - # any GUI stuff. - if fslTool.interface is not None: - import wx - app = wx.App() + # GUI or command-line tool? + if fslTool.interface is not None: _runGUITool(fslTool, toolArgv) + else: _runCLITool(fslTool, toolArgv) + + +def _runGUITool(fslTool, toolArgv): + """Runs the given ``FSLTool``, which is assumed to be a GUI tool. + + :arg fslTool: The ``FSLTool`` to run - see the :func:`_loadFSLTool` + function. + + :arg toolArgv: Unparsed tool-specific command line arguments. + """ + import wx + + fslEnvActive = 'FSLDIR' in os.environ + + # Create a wx.App before init(), + # in case it does GUI stuff. + app = wx.App() # Call the tool's init # function if there is one if fslTool.init is not None: initVal = fslTool.init() else: initVal = None - # Parse the tool-specific - # command line arguments - toolNamespace = _parseToolArgs(fslTool, namespace, toolArgv) + # 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, e.g. + # FSLEyes, which displays status updates + # to the user while it is loading overlays + # and setting up the interface. + # + # To make this work, this buildGUI + # function is called on a separate thread + # (so it is executed after wx.MainLoop + # has been called), but it schedules its + # work to be done on the wx.MainLoop. + def buildGUI(): + def realBuild(): - # Is this a GUI tool? - if fslTool.interface is not None: + # Parse the tool-specific + # command line arguments + toolNamespace = _parseToolArgs(fslTool, toolArgv) - import wx + # Call the tool context function + if fslTool.context is not None: + ctx = fslTool.context(toolNamespace, initVal) + else: + ctx = None - # The main interface is created on the - # wx.MainLoop, because it is difficult - # to force immediate GUI refreshes when - # not running on the main loop - this - # is important for, e.g. FSLEyes, which - # displays status updates to the user - # while it is loading overlays and - # setting up the interface. - # - # To make this work, this buildGUI - # function is called on a separate thread - # (so it is executed after wx.MainLoop - # has been called), but it schedules its - # work to be done on the wx.MainLoop. - def buildGUI(): - def realBuild(): - - if fslTool.context is not None: - ctx = fslTool.context(toolNamespace, initVal) - else: - ctx = None - - frame = _buildGUI(toolNamespace, fslTool, ctx, fslEnvActive) - frame.Show() - - # See comment below - dummyFrame.Destroy() - - _fslDirWarning(frame, fslTool.toolName, fslEnvActive) - - if namespace.wxinspect: - import wx.lib.inspection - wx.lib.inspection.InspectionTool().Show() - - time.sleep(0.1) - wx.CallAfter(realBuild) - - # Create the wx.App object, and create a dummy - # frame. If we don't create a dummy frame, the - # wx.MainLoop call will just return immediately. - # The buildGUI function above will kill the dummy - # frame when it has created the real interface. - dummyFrame = wx.Frame(None) - - threading.Thread(target=buildGUI).start() - - # The wx.App was created above, - # before calling fslTool.init() - app.MainLoop() - - # Or is this a CLI tool? - elif fslTool.execute is not None: - - if fslTool.context is not None: - ctx = fslTool.context(toolNamespace, initVal) - else: - ctx = None + # Build the GUI + frame = _buildGUI(toolNamespace, fslTool, ctx, fslEnvActive) + frame.Show() + + # See comment about the + # dummy frame below + dummyFrame.Destroy() + + _fslDirWarning(frame, fslTool.toolName, fslEnvActive) + + time.sleep(0.1) + wx.CallAfter(realBuild) + + # Create the wx.App object, and create a dummy + # frame. If we don't create a dummy frame, the + # wx.MainLoop call will just return immediately. + # The buildGUI function above will kill the dummy + # frame when it has created the real interface. + dummyFrame = wx.Frame(None) + + threading.Thread(target=buildGUI).start() + + # The wx.App was created above, + # before calling fslTool.init() + app.MainLoop() + + +def _runCLITool(fslTool, toolArgv): + """Runs the given ``FSLTool``, which is assumed to be a command-line (i.e. + non-GUI) tool. + + :arg fslTool: The ``FSLTool`` to run - see the :func:`_loadFSLTool` + function. + + :arg toolArgv: Unparsed tool-specific command line arguments. + """ + + if fslTool.execute is None: + return + + # Call the tool's init + # function if there is one + if fslTool.init is not None: initVal = fslTool.init() + else: initVal = None + + # Parse the tool-specific + # command line arguments + namespace = _parseToolArgs(fslTool, toolArgv) + + initVal = None + ctx = None + + if fslTool.init is not None: initVal = fslTool.init() + if fslTool.context is not None: ctx = fslTool.context(namespace, + initVal) - _fslDirWarning(None, fslTool.toolName, fslEnvActive) - fslTool.execute(toolNamespace, ctx) + _fslDirWarning(None, fslTool.toolName, 'FSLDIR' in os.environ) + fslTool.execute(namespace, ctx) def runTool(toolName, args, **kwargs): @@ -343,10 +369,6 @@ def _parseTopLevelArgs(argv, allTools): '-m', '--memory', action='store_true', help='Output memory events (implied if -v is set)') - parser.add_argument( - '-w', '--wxinspect', action='store_true', - help='Run wx inspection tool') - parser.add_argument('tool', help='FSL program to run', nargs='?') # No arguments at all? @@ -481,16 +503,12 @@ def _parseTopLevelArgs(argv, allTools): return fslTool, namespace, toolArgv -def _parseToolArgs(tool, namespace, argv): +def _parseToolArgs(tool, argv): """Parses tool-specific command-line arguments. Returns the result of calling the ``FSL_PARSEARGS`` attribute of the given tool, or ``None`` if the tool does not have the function. :arg tool: The ``FSLTool`` to be invoked. - - :arg namespace: The ``argparse.Namespace`` object containing parsed - top-level arguments. - :arg argv: Command line arguments to be parsed. """ diff --git a/fsl/fsleyes/actions/about.py b/fsl/fsleyes/actions/about.py index 651e241ca..3ac176406 100644 --- a/fsl/fsleyes/actions/about.py +++ b/fsl/fsleyes/actions/about.py @@ -9,8 +9,7 @@ displays an about dialog for *FSLeyes*. """ -import action -import fsl.fsleyes.about as aboutdlg +import action class AboutAction(action.Action): @@ -36,6 +35,8 @@ class AboutAction(action.Action): def __showDialog(self): """Creates and shows an :class:`.AboutDialog`. """ + import fsl.fsleyes.about as aboutdlg + dlg = aboutdlg.AboutDialog(self.__frame) dlg.Show() dlg.CentreOnParent() diff --git a/fsl/tools/fsleyes.py b/fsl/tools/fsleyes.py index 4fa38e51a..330ba5b49 100644 --- a/fsl/tools/fsleyes.py +++ b/fsl/tools/fsleyes.py @@ -25,11 +25,10 @@ import logging import textwrap import argparse -import fsl.fsleyes.fsleyes_parseargs as fsleyes_parseargs -import fsl.fsleyes.perspectives as perspectives -import fsl.utils.status as status -import fsl.utils.async as async -import fsl.data.strings as strings +import fsl.fsleyes.perspectives as perspectives +import fsl.utils.status as status +import fsl.utils.async as async +import fsl.data.strings as strings log = logging.getLogger(__name__) @@ -58,6 +57,8 @@ def parseArgs(argv): :arg argv: command line arguments for ``fsleyes``. """ + import fsl.fsleyes.fsleyes_parseargs as fsleyes_parseargs + parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.RawDescriptionHelpFormatter) @@ -111,9 +112,10 @@ def context(args, splash): - the :class:`.FSLEyesSplash` frame """ - import fsl.fsleyes.overlay as fsloverlay - import fsl.fsleyes.displaycontext as displaycontext - import fsl.fsleyes.gl as fslgl + import fsl.fsleyes.overlay as fsloverlay + import fsl.fsleyes.fsleyes_parseargs as fsleyes_parseargs + import fsl.fsleyes.displaycontext as displaycontext + import fsl.fsleyes.gl as fslgl import props props.initGUI() @@ -187,9 +189,10 @@ def interface(parent, args, ctx): :returns: the :class:`.FSLEyesFrame` that was created. """ - import wx - import fsl.fsleyes.frame as fsleyesframe - import fsl.fsleyes.views as views + import wx + import fsl.fsleyes.fsleyes_parseargs as fsleyes_parseargs + import fsl.fsleyes.frame as fsleyesframe + import fsl.fsleyes.views as views overlayList, displayCtx, splashFrame = ctx -- GitLab