#!/usr/bin/env python # # platform.py - Platform information # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # """This module provides the :class:`Platform` class, which is a container of information about the current platform we are running on. A single ``Platform`` instance is created when this module is first imported, and is available as a module attribute called :attr:`platform`. This module is also home to the following utility functions which abstract away various platform differences: .. autosummary:: :nosignatures: isWidgetAlive """ import logging import os import sys import importlib import fsl.utils.notifier as notifier # An annoying consequence of using # a system-module name for our own # module is that we can't import # it directly (as it will attempt # to import itself, i.e. this module). # # This is only necessary in Python 2.x # (as python 3 disallows relative # imports). builtin_platform = importlib.import_module('platform') log = logging.getLogger(__name__) WX_UNKNOWN = 0 """Identifier for the :attr:`Platform.wxFlavour` and :attr:`Platform.wxPlatform` properties indicating an unknown/undetermined flavour/platform. """ WX_PYTHON = 1 """Identifier for the :attr:`Platform.wxFlavour` property, indicating that we are running standard wx Python. """ WX_PHOENIX = 2 """Identifier for the :attr:`Platform.wxFlavour` property, indicating that we are running wx Python/Phoenix. """ WX_MAC_COCOA = 1 """Identifier for the :attr:`Platform.wxPlatform` property, indicating that we are running the OSX cocoa wx build. """ WX_MAC_CARBON = 2 """Identifier for the :attr:`Platform.wxPlatform` property, indicating that we are running the OSX carbon wx build. """ WX_GTK = 3 """Identifier for the :attr:`Platform.wxPlatform` property, indicating that we are running the Linux/GTK wx build. """ def isWidgetAlive(widget): """Returns ``True`` if the given ``wx.Window`` object is "alive" (i.e. has not been destroyed), ``False`` otherwise. Works in both wxPython and wxPython/Phoenix. """ import wx if platform.wxFlavour == WX_PHOENIX: return bool(widget) elif platform.wxFlavour == WX_PYTHON: try: widget.IsEnabled() return True except wx.PyDeadObjectError: return False class Platform(notifier.Notifier): """The ``Platform`` class contains a handful of properties which contain information about the platform we are running on. .. note:: The values of the :attr:`glVersion` and :attr:`glRenderer` properties are not automatically set - they will only contain a value if one is assigned to them. *FSLeyes* does this during startup, in the :func:`fsleyes.gl.bootstrap` function. .. autosummary:: os frozen fsldir haveGui canHaveGui inSSHSession wxPlatform wxFlavour glVersion glRenderer glIsSoftwareRenderer """ def __init__(self): """Create a ``Platform`` instance. """ # For things which 'from fsl.utils.platform import platform', # these identifiers are available on the platform instance self.WX_UNKNOWN = WX_UNKNOWN self.WX_PYTHON = WX_PYTHON self.WX_PHOENIX = WX_PHOENIX self.WX_MAC_COCOA = WX_MAC_COCOA self.WX_MAC_CARBON = WX_MAC_CARBON self.WX_GTK = WX_GTK self.isWidgetAlive = isWidgetAlive self.__fsldir = os.environ.get('FSLDIR', None) self.__inSSHSession = False self.__glVersion = None self.__glRenderer = None self.__glIsSoftware = None # Determine if a display is available. We do # this once at init (instead of on-demand in # the canHaveGui method) because calling the # IsDisplayAvailable function will cause the # application to steal focus under OSX! try: import wx self.__canHaveGui = wx.App.IsDisplayAvailable() except ImportError: self.__canHaveGui = False # If one of these environment # variables is set, then we're # probably running over SSH. self.__inSSHSession = 'SSH_CLIENT' in os.environ or \ 'SSH_TTY' in os.environ @property def os(self): """The operating system name. Whatever is returned by the built-in ``platform.system`` function. """ return builtin_platform.system() @property def frozen(self): """``True`` if we are running in a compiled/frozen application, ``False`` otherwise. """ return getattr(sys, 'frozen', False) @property def haveGui(self): """``True`` if we are running with a GUI, ``False`` otherwise. """ try: import wx app = wx.GetApp() return (self.canHaveGui and app is not None and app.IsMainLoopRunning()) except ImportError: return False @property def canHaveGui(self): """``True`` if it is possible to create a GUI, ``False`` otherwise. """ return self.__canHaveGui @property def inSSHSession(self): """``True`` if this application is running over an SSH session, ``False`` otherwise. """ return self.__inSSHSession @property def wxPlatform(self): """One of :data:`WX_UNKNOWN`, :data:`WX_MAC_COCOA`, :data:`WX_MAC_CARBON`, or :data:`WX_GTK`, indicating the wx platform. """ if not self.haveGui: return WX_UNKNOWN import wx pi = [t.lower() for t in wx.PlatformInfo] for tag in pi: if any(['cocoa' in p for p in pi]): platform = WX_MAC_COCOA elif any(['carbon' in p for p in pi]): platform = WX_MAC_CARBON elif any(['gtk' in p for p in pi]): platform = WX_GTK else: platform = WX_UNKNOWN if platform is WX_UNKNOWN: log.warning('Could not determine wx platform from ' 'information: {}'.format(pi)) return platform @property def wxFlavour(self): """One of :data:`WX_UNKNOWN`, :data:`WX_PYTHON` or :data:`WX_PHOENIX`, indicating the wx flavour. """ if not self.haveGui: return WX_UNKNOWN import wx pi = [t.lower() for t in wx.PlatformInfo] isPhoenix = False for tag in pi: if 'phoenix' in tag: isPhoenix = True break if isPhoenix: return WX_PHOENIX else: return WX_PYTHON @property def fsldir(self): """The FSL installation location. .. note:: The ``fsldir`` property can be updated - when it is changed, any registered listeners are notified via the :class:`.Notifier` interface. """ return self.__fsldir @fsldir.setter def fsldir(self, value): """Changes the value of the :attr:`fsldir` property, and notifies any registered listeners. """ self.__fsldir = value os.environ['FSLDIR'] = value self.notify() @property def glVersion(self): """Returns the available OpenGL version, or ``None`` if it has not been set. """ return self.__glVersion @glVersion.setter def glVersion(self, value): """Set the available OpenGL version. """ self.__glVersion = value @property def glRenderer(self): """Returns the available OpenGL renderer, or ``None`` if it has not been set. """ return self.__glRenderer @glRenderer.setter def glRenderer(self, value): """Set the available OpenGL renderer. """ self.__glRenderer = value value = value.lower() # There doesn't seem to be any quantitative # method for determining whether we are using # software-based rendering, so a hack is # necessary. self.__glIsSoftware = any(( 'software' in value, 'mesa' in value, 'gallium' in value, 'llvmpipe' in value, 'chromium' in value, )) @property def glIsSoftwareRenderer(self): """Returns ``True`` if the OpenGL renderer is software based, ``False`` otherwise, or ``None`` if the renderer has not yet been set. .. note:: This check is based on heuristics, ans is not guaranteed to be correct. """ return self.__glIsSoftware platform = Platform() """An instance of the :class:`Platform` class. Feel free to create your own instance, but be aware that if you do so you will not be updated of changes to the :attr:`Platform.fsldir` property. """