Forked from
FSL / fslpy
2468 commits behind the upstream repository.
-
Paul McCarthy authoredPaul McCarthy authored
platform.py 10.02 KiB
#!/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 os.path as op
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:
# GetId seems to be available on all wx
# objects, despite not being documented.
#
# I was originally calling IsEnabled,
# but this causes segfaults if called
# on a wx.MenuItem from within an
# event handler on that menu item!
widget.GetId()
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.__inSSHSession = False
self.__glVersion = None
self.__glRenderer = None
self.__glIsSoftware = None
self.__fslVersion = None
self.fsldir = os.environ.get('FSLDIR', 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.
"""
if value is not None:
value = value.strip()
if value is None: pass
elif value == '': value = None
elif not op.exists(value): value = None
elif not op.isdir(value): value = None
self.__fsldir = value
if value is not None:
os.environ['FSLDIR'] = value
# Set the FSL version field if we can
versionFile = op.join(value, 'etc', 'fslversion')
if op.exists(versionFile):
with open(versionFile, 'rt') as f:
self.__fslVersion = f.read().strip()
self.notify()
@property
def fslVersion(self):
"""Returns the FSL version as a string, e.g. ``'5.0.9'``. Returns
``None`` if a FSL installation could not be found.
"""
return self.__fslVersion
@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.
"""