#
# layout.py - The layout API (previously called "perspectives").
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides functions for managing *layouts* - stored view and
control panel layouts for *FSLeyes*. Layouts may be persisted using the
:mod:`.settings` module. A few layouts are also *built in*, and are
defined in the :attr:`BUILT_IN_LAYOUTS` dictionary.
.. note:: Prior to FSLeyes 0.24.0, *layouts* were called *perspectives*.
The ``layouts`` module provides the following functions. These are intended
for use by the :class:`.FSLeyesFrame`, but can be used in other ways too:
.. autosummary::
:nosignatures:
getAllLayouts
loadLayout
applyLayout
saveLayout
removeLayout
serialiseLayout
deserialiseLayout
A layout defines a layout for a :class:`.FSLeyesFrame`. It specifies the
type and layout of one or more *views* (defined in the :mod:`.views` module)
and, within each view, the type and layout of one or more *controls* (defined
in the :mod:`.controls` module). See the :mod:`fsleyes` documentation for
an overview of views and controls.
All of this information is stored as a string - see the
:func:`serialiseLayout` function for details on its storage format.
"""
import functools as ft
import logging
import pkgutil
import textwrap
import importlib
import collections
import fsl.utils.settings as fslsettings
import fsleyes_widgets.utils.status as status
import fsleyes.strings as strings
import fsleyes.plugins as plugins
import fsleyes.controls as controls
import fsleyes.views as views
import fsleyes.views.viewpanel as viewpanel
import fsleyes.views.canvaspanel as canvaspanel
import fsleyes.views.plotpanel as plotpanel
import fsleyes.controls.controlpanel as controlpanel
log = logging.getLogger(__name__)
[docs]def getAllLayouts():
"""Returns a list containing the names of all saved layouts. The
returned list does not include built-in layouts - these are
accessible in the :attr:`BUILT_IN_LAYOUTS` dictionary.
"""
layouts = fslsettings.read('fsleyes.layouts', []) + \
fslsettings.read('fsleyes.perspectives', [])
uniq = []
for l in layouts:
if l not in uniq:
uniq.append(l)
return uniq
[docs]def loadLayout(frame, name, **kwargs):
"""Load the named layout, and apply it to the given
:class:`.FSLeyesFrame`. The ``kwargs`` are passed through to the
:func:`applyLayout` function.
"""
if name in BUILT_IN_LAYOUTS.keys():
log.debug('Loading built-in layout {}'.format(name))
layout = BUILT_IN_LAYOUTS[name]
else:
log.debug('Loading saved layout {}'.format(name))
layout = fslsettings.read('fsleyes.layouts.{}'.format(name), None)
if layout is None:
fslsettings.read('fsleyes.perspectives.{}'.format(name), None)
if layout is None:
raise ValueError('No layout named "{}" exists'.format(name))
log.debug('Applying layout:\n{}'.format(layout))
applyLayout(frame, name, layout, **kwargs)
[docs]def applyLayout(frame, name, layout, message=None):
"""Applies the given serialised layout string to the given
:class:`.FSLeyesFrame`.
:arg frame: The :class:`.FSLeyesFrame` instance.
:arg name: The layout name.
:arg layout: The serialised layout string.
:arg message: A message to display (using the :mod:`.status` module).
"""
import fsleyes.views.canvaspanel as canvaspanel
layout = deserialiseLayout(layout)
frameChildren = layout[0]
frameLayout = layout[1]
vpChildrens = layout[2]
vpLayouts = layout[3]
vpPanelProps = layout[4]
vpSceneProps = layout[5]
# Show a message while re-configuring the frame
if message is None:
message = strings.messages[
'layout.applyingLayout'].format(
strings.layouts.get(name, name))
status.update(message)
# Clear all existing view
# panels from the frame
frame.removeAllViewPanels()
# Add all of the view panels
# specified in the layout
for vp in frameChildren:
log.debug('Adding view panel {} to frame'.format(vp.__name__))
frame.addViewPanel(vp, defaultLayout=False)
# Apply the layout to those view panels
frame.auiManager.LoadPerspective(frameLayout)
# For each view panel, add all of the
# control panels, and lay them out
viewPanels = frame.viewPanels
for i in range(len(viewPanels)):
vp = viewPanels[ i]
children = vpChildrens[ i]
vpLayout = vpLayouts[ i]
panelProps = vpPanelProps[i]
sceneProps = vpSceneProps[i]
for child in children:
log.debug('Adding control panel {} to {}'.format(
child.__name__, type(vp).__name__))
_addControlPanel(vp, child)
vp.auiManager.LoadPerspective(vpLayout)
# Apply saved property values
# to the view panel.
for name, val in panelProps.items():
log.debug('Setting {}.{} = {}'.format(
type(vp).__name__, name, val))
vp.deserialise(name, val)
# And to its SceneOpts instance if
# it is a CanvasPanel, or its
# PlotCanvas if it is a PlotPanel
if isinstance(vp, canvaspanel.CanvasPanel): aux = vp.sceneOpts
elif isinstance(vp, plotpanel.PlotPanel): aux = vp.canvas
for name, val in sceneProps.items():
log.debug('Setting {}.{} = {}'.format(
type(aux).__name__, name, val))
aux.deserialise(name, val)
[docs]def saveLayout(frame, name):
"""Serialises the layout of the given :class:`.FSLeyesFrame` and saves
it as a layout with the given name.
"""
if name in BUILT_IN_LAYOUTS.keys():
raise ValueError('A built-in layout named "{}" '
'already exists'.format(name))
log.debug('Saving current layout with name {}'.format(name))
layout = serialiseLayout(frame)
fslsettings.write('fsleyes.layouts.{}'.format(name), layout)
_addToLayoutList(name)
log.debug('Serialised layout:\n{}'.format(layout))
[docs]def removeLayout(name):
"""Deletes the named layout. """
log.debug('Deleting layout with name {}'.format(name))
fslsettings.delete('fsleyes.layouts.{}' .format(name))
fslsettings.delete('fsleyes.perspectives.{}'.format(name))
_removeFromLayoutList(name)
[docs]def serialiseLayout(frame):
"""Serialises the layout of the given :class:`.FSLeyesFrame`, and returns
it as a string.
.. note:: This function was written against wx.lib.agw.aui.AuiManager as
it exists in wxPython 3.0.2.0.
*FSLeyes* uses a hierarchy of ``wx.lib.agw.aui.AuiManager`` instances for
its layout - the :class:`.FSLeyesFrame` uses an ``AuiManager`` to lay out
:class:`.ViewPanel` instances, and each of these ``ViewPanels`` use their
own ``AuiManager`` to lay out control panels.
The layout for a single ``AuiManager`` can be serialised to a string via
the ``AuiManager.SavePerspective`` and ``AuiManager.SavePaneInfo``
methods. One of these strings consists of:
- A name, `'layout1'` or `'layout2'`, specifying the AUI version
(this will always be at least `'layout2'` for *FSLeyes*).
- A set of key-value set of key-value pairs defining the top level
panel layout.
- A set of key-value pairs for each pane, defining its layout. the
``AuiManager.SavePaneInfo`` method returns this for a single pane.
These are all encoded in a single string, with the above components
separated with '|' characters, and the pane-level key-value pairs
separated with a ';' character. For example:
layout2|key1=value1|name=Pane1;caption=Pane 1|\
name=Pane2;caption=Pane 2|doc_size(5,0,0)=22|
This function queries each of the AuiManagers, and extracts the following:
1. A layout string for the :class:`.FSLeyesFrame`.
2. A string containing a comma-separated list of :class:`.ViewPanel`
class names, in the same order as they are specified in the frame
layout string.
3. For each ``ViewPanel``:
- A layout string for the ``ViewPanel``
- A string containing a comma-separated list of control panel
class names, in the same order as specified in the
``ViewPanel`` layout string.
Each of these pieces of information are then concatenated into a single
newline separated string.
In FSLeyes 0.35.0, the list of ``ViewPanel`` and ``ControlPanel`` class
names was changed from containing just the class names
(e.g. ``'OrthoPanel'``) to containing the fully resolved class paths
(e.g. ``'fsleyes.views.orthopanel.OrthoPanel'``). The
:func:`deserialiseLayout` function is compatible with both formats.
"""
# We'll start by defining this silly function, which
# takes an ``AuiManager`` layout string, and a list
# of the children which are being managed by the
# AuiManager, and makes sure that the order of the
# child pane layout specifications in the string is
# the same as the order of the children in the list.
#
# If the 'rename' argument is True, this function
# performs an additional step.
#
# The FSLeyesFrame gives each of its view panels a
# unique name of the form "ClassName index", where
# the 'index' is a sequentially increasing identifier
# number (so that multiple views of the same type can
# be differentiated). If the 'rename' argument to
# this function is True, these names are adjusted so
# that they begin at 1 and increase sequentially. This
# is done by the patchPanelName function, defined
# below.
#
# This name adjustment is required to handle
# situations where the indices of existing view panels
# are not sequential, as when a layout is applied, the
# view panel names given by the FSLeyesFrame must
# match the names that are specified in the layout
# string.
#
# In addition to patching the name of each panel,
# the 'rename' argument will also cause the panel
# caption (its display title) to be adjusted so that
# it is in line with the name.
def patchLayoutString(auiMgr, panels, rename=False):
layoutStr = auiMgr.SavePerspective()
# The different sections of the string
# returned by SavePerspective are
# separated with a '|' character.
sections = layoutStr.split('|')
sections = [s.strip() for s in sections]
sections = [s for s in sections if s != '']
# Here, we identify sections which specify
# the layout of a child pane, remove them,
# and patch them back in, in the order that
# the child panels are specified in the list.
pi = 0
for si, s in enumerate(sections):
if s.find('name=') > -1:
panel = panels[pi]
panelInfo = auiMgr.GetPane(panel)
panelLayout = auiMgr.SavePaneInfo(panelInfo)
pi += 1
sections[si] = panelLayout
if rename:
sections[si] = patchPanelName(sections[si], pi)
# Now the panel layouts in our layout string
# are in the same order as our list of view
# panels - we can re-join the layout string
# sections, and we're done.
return '|'.join(sections) + '|'
# The purpose of this function is described above.
def patchPanelName(layoutString, index):
# In each AUI layout section, 'key=value'
# pairs are separated with a semi-colon
kvps = layoutString.split(';')
# And each 'key=value' pair is separated
# with an equals character
kvps = [kvp.split('=') for kvp in kvps]
kvps = collections.OrderedDict(kvps)
# We need to update the indices contained
# in the 'name' and 'caption' values
name = kvps['name']
caption = kvps['caption']
# Strip off the old index
name = ' '.join(name .split()[:-1])
caption = ' '.join(caption.split()[:-1])
# Patch in the new index
name = '{} {}'.format(name, index)
caption = '{} {}'.format(caption, index)
kvps['name'] = name
kvps['caption'] = caption
# Reconstruct the layout string
kvps = ['='.join((k, v)) for k, v in kvps.items()]
kvps = ';'.join(kvps)
return kvps
# Now we can start extracting the layout information.
# We start with the FSLeyesFrame layout.
auiMgr = frame.auiManager
viewPanels = frame.viewPanels
# Generate the frame layout string, and a
# list of the children of the frame
frameLayout = patchLayoutString(auiMgr, viewPanels, True)
frameChildren = ['.'.join((type(vp).__module__, type(vp).__qualname__))
for vp in viewPanels]
frameChildren = ','.join(frameChildren)
# We are going to build a list of layout strings,
# one for each ViewPanel, and a corresponding list
# of control panels displayed on each ViewPanel.
vpLayouts = []
vpConfigs = []
for vp in viewPanels:
# Get the auiManager and layout for this view panel.
# This is a little bit complicated, as ViewPanels
# differentiate between the main 'centre' panel, and
# all other secondary (control) panels. The layout
# string needs to contain layout information for
# all of these panels, but we only care about the
# control panels.
vpAuiMgr = vp.auiManager
ctrlPanels = vp.getPanels()
centrePanel = vp.centrePanel
# As above for the frame, generate a layout
# string and a list of control panels - the
# children of the view panel.
vpLayout = patchLayoutString(vpAuiMgr, [centrePanel] + ctrlPanels)
vpChildren = ['.'.join((type(cp).__module__, type(cp).__qualname__))
for cp in ctrlPanels]
vpChildren = ','.join(vpChildren)
# Get the panel and scene settings
panelProps, sceneProps = _getPanelProps(vp)
# And turn them into comma-separated key-value pairs.
panelProps = ['{}={}'.format(k, v) for k, v in panelProps.items()]
sceneProps = ['{}={}'.format(k, v) for k, v in sceneProps.items()]
panelProps = ','.join(panelProps)
sceneProps = ','.join(sceneProps)
# Build the config string - the children,
# the panel settings and the scene settings.
vpConfig = ';'.join([vpChildren, panelProps, sceneProps])
vpLayouts.append(vpLayout)
vpConfigs.append(vpConfig)
# We serialise all of these pieces of information
# as a single newline-separated string.
layout = [frameChildren, frameLayout]
for vpConfig, vpLayout in zip(vpConfigs, vpLayouts):
layout.append(vpConfig)
layout.append(vpLayout)
# And we're done!
return '\n'.join(layout)
[docs]def deserialiseLayout(layout):
"""Deserialises a layout string which was created by the
:func:`serialiseLayout` string.
:returns: A tuple containing the following:
- A list of :class:`.ViewPanel` class types - the
children of the :class:`.FSLeyesFrame`.
- An ``aui`` layout string for the :class:`.FSLeyesFrame`
- A list of lists, one for each ``ViewPanel``, with each
list containing a collection of control panel class
types - the children of the corresponding ``ViewPanel``.
- A list of strings, one ``aui`` layout string for each
``ViewPanel``.
- A list of dictionaries, one for each ``ViewPanel``,
containing property ``{name : value}`` pairs to be
applied to the ``ViewPanel``.
- A list of dictionaries, one for each ``ViewPanel``,
containing property ``{name : value}`` pairs to be applied
to the :class:`.SceneOpts` instance associated with the
``ViewPanel``, if it is a :class:`.CanvasPanel`, or the
:class:`.PlotCanvas` instance associated with the
``ViewPanel``, if it is a :class:`.PlotPanel`.
"""
# Versions of FSLeyes prior to 1.0.0 would just
# save the view/control class name. This was
# changed in 1.0.0 so that the full path to the
# class is saved. This function aims to be
# compatible with both formats - given a class
# name, or a fully resolved class name, it will
# return the corresponding type object.
def findViewOrControl(panelname, paneltype):
# new format
if '.' in panelname:
mod, cls = panelname.rsplit('.', maxsplit=1)
mod = importlib.import_module(mod)
return getattr(mod, cls)
# make a list of all candidate types,
# then search through them for a match
panels = []
# builtins
if paneltype == 'control':
basemod = controls
basetype = (controlpanel.ControlPanel,
controlpanel.ControlToolBar)
else:
basemod = views
basetype = viewpanel.ViewPanel
mods = pkgutil.iter_modules(basemod.__path__, basemod.__name__ + '.')
for _, mod, _ in mods:
mod = importlib.import_module(mod)
for att in dir(mod):
att = getattr(mod, att)
if isinstance(att, type) and issubclass(att, basetype):
panels.append(att)
# plugins
if paneltype == 'control':
panels.extend(plugins.listControls().values())
else:
panels.extend(plugins.listViews().values())
for panel in panels:
if panel.__name__ == panelname:
return panel
raise ValueError('Unknown FSLeyes panel type: {}'.format(panelname))
findView = ft.partial(findViewOrControl, paneltype='view')
findControl = ft.partial(findViewOrControl, paneltype='control')
lines = layout.split('\n')
lines = [line.strip() for line in lines]
lines = [line for line in lines if line != '']
frameChildren = lines[0]
frameLayout = lines[1]
# The children strings are comma-separated
# class names. The frame children are ViewPanels,
# which are all defined in the fsleyes.views
# package.
frameChildren = frameChildren.split(',')
frameChildren = [fc.strip() for fc in frameChildren]
frameChildren = [fc for fc in frameChildren if fc != '']
frameChildren = [findView(fc) for fc in frameChildren]
# Collate the children/layouts for each view panel
vpChildren = []
vpLayouts = []
vpPanelProps = []
vpSceneProps = []
for i in range(len(frameChildren)):
linei = (i * 2) + 2
config = lines[linei]
layout = lines[linei + 1]
children, panelProps, sceneProps = config.split(';')
vpChildren .append(children)
vpLayouts .append(layout)
vpPanelProps .append(panelProps)
vpSceneProps .append(sceneProps)
# The ViewPanel children string is a comma-separated
# list of control panel class names. All control panels
# should be defined in the fsleyes.controls package.
for i in range(len(vpChildren)):
children = vpChildren[i].split(',')
children = [vpc.strip() for vpc in children]
children = [vpc for vpc in children if vpc != '']
children = [findControl(vpc) for vpc in children]
vpChildren[i] = children
# The panel props and scene props strings are
# comma-separated lists of 'prop=value' pairs.
# We'll turn them into a dict for convenience.
for i in range(len(vpPanelProps)):
props = vpPanelProps[i].split(',')
props = [p for p in props if p != '']
props = [p.split('=') for p in props]
vpPanelProps[i] = collections.OrderedDict(props)
for i in range(len(vpSceneProps)):
props = vpSceneProps[i].split(',')
props = [p for p in props if p != '']
props = [p.split('=') for p in props]
vpSceneProps[i] = collections.OrderedDict(props)
return (frameChildren,
frameLayout,
vpChildren,
vpLayouts,
vpPanelProps,
vpSceneProps)
[docs]def _addToLayoutList(layout):
"""Adds the given layout name to the list of saved layouts. """
layout = layout.strip()
layouts = getAllLayouts()
if layout not in layouts:
layouts.append(layout)
log.debug('Updating stored layout list: {}'.format(layout))
fslsettings.write('fsleyes.layouts', layouts)
[docs]def _removeFromLayoutList(layout):
"""Removes the given layout name from the list of saved layouts.
"""
layouts = getAllLayouts()
try: layouts.remove(layout)
except ValueError: return
log.debug('Updating stored layout list: {}'.format(layouts))
fslsettings.write('fsleyes.layouts', layouts)
[docs]def _addControlPanel(viewPanel, panelType):
"""Adds a control panel to the given :class:`.ViewPanel`.
:arg viewPanel: A :class:`.ViewPanel` instance.
:arg panelType: A control panel type.
"""
viewPanel.togglePanel(panelType)
[docs]def _getPanelProps(panel):
"""Creates and returns two dictionaries, containing properties of the given
:class:`.ViewPanel` (and its associated :class:`.SceneOpts` instance, if
it is a :class:`.CanvasPanel`, or :class:`.PlotCanvas`, if it is a
:class:`.PlotPanel`), which are to be saved as part of a seriaised
*FSLeyes* layout. The properties to be saved are listed in the
:data:`VIEWPANEL_PROPS` dictionary.
"""
if not isinstance(panel, (canvaspanel.CanvasPanel, plotpanel.PlotPanel)):
return {}, {}
panelType = type(panel).__name__
panelProps, sceneProps = VIEWPANEL_PROPS.get(panelType, ({}, {}))
if isinstance(panel, canvaspanel.CanvasPanel):
aux = panel.sceneOpts
elif isinstance(panel, plotpanel.PlotPanel):
aux = panel.canvas
panelProps = {name : panel.serialise(name) for name in panelProps}
sceneProps = {name : aux .serialise(name) for name in sceneProps}
return panelProps, sceneProps
VIEWPANEL_PROPS = {
'OrthoPanel' : [['syncLocation',
'syncOverlayOrder',
'syncOverlayDisplay',
'syncOverlayVolume',
'movieRate',
'movieAxis'],
['showCursor',
'bgColour',
'fgColour',
'cursorColour',
'cursorGap',
'showColourBar',
'colourBarLocation',
'colourBarLabelSide',
'showXCanvas',
'showYCanvas',
'showZCanvas',
'showLabels',
'labelSize',
'layout',
'xzoom',
'yzoom',
'zzoom',
'highDpi']],
'LightBoxPanel' : [['syncLocation',
'syncOverlayOrder',
'syncOverlayDisplay',
'syncOverlayVolume',
'movieRate',
'movieAxis'],
['showCursor',
'bgColour',
'fgColour',
'cursorColour',
'showColourBar',
'colourBarLocation',
'colourBarLabelSide',
'zax',
'showGridLines',
'highlightSlice',
'highDpi']],
'Scene3DPanel' : [['syncLocation',
'syncOverlayOrder',
'syncOverlayDisplay',
'syncOverlayVolume'],
['showCursor',
'bgColour',
'fgColour',
'cursorColour',
'showColourBar',
'colourBarLocation',
'colourBarLabelSide',
'occlusion',
'light',
'lightPos',
'offset',
'rotation',
'showLegend']],
'TimeSeriesPanel' : [['usePixdim',
'plotMode',
'plotMelodicICs'],
['legend',
'xAutoScale',
'yAutoScale',
'xLogScale',
'yLogScale',
'ticks',
'grid',
'gridColour',
'bgColour',
'smooth']],
'HistogramPanel' : [['histType',
'plotType'],
['legend',
'xAutoScale',
'yAutoScale',
'xLogScale',
'yLogScale',
'ticks',
'grid',
'gridColour',
'bgColour',
'smooth']],
'PowerSpectrumPanel' : [['plotMelodicICs',
'plotFrequencies'],
['legend',
'xAutoScale',
'yAutoScale',
'xLogScale',
'yLogScale',
'ticks',
'grid',
'gridColour',
'bgColour',
'smooth']]}
# The order in which properties are defined in
# a layout is the order in which they will
# be applied. This is important to remember when
# considering properties that have side effects
# (e.g. setting SceneOpts.bgColour will clobber
# SceneOpts.fgColour).
BUILT_IN_LAYOUTS = collections.OrderedDict((
('default',
textwrap.dedent("""
fsleyes.views.orthopanel.OrthoPanel
layout2|name=OrthoPanel 1;caption=Ortho View 1;state=67376064;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|
fsleyes.controls.orthotoolbar.OrthoToolBar,fsleyes.controls.overlaydisplaytoolbar.OverlayDisplayToolBar,fsleyes.controls.overlaylistpanel.OverlayListPanel,fsleyes.controls.locationpanel.LocationPanel;syncOverlayOrder=True,syncLocation=True,syncOverlayDisplay=True,movieRate=400;colourBarLocation=top,showCursor=True,bgColour=#000000ff,layout=horizontal,colourBarLabelSide=top-left,cursorGap=False,fgColour=#ffffffff,cursorColour=#00ff00ff,showXCanvas=True,showYCanvas=True,showColourBar=False,showZCanvas=True,showLabels=True
layout2|name=Panel;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OrthoToolBar;caption=Ortho view toolbar;state=67382012;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OverlayDisplayToolBar;caption=Display toolbar;state=67382012;dir=1;layer=11;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OverlayListPanel;caption=Overlay list;state=67373052;dir=3;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=1;minh=1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=LocationPanel;caption=Location;state=67373052;dir=3;layer=0;row=0;pos=1;prop=100000;bestw=-1;besth=-1;minw=1;minh=1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|dock_size(3,0,0)=176|dock_size(1,10,0)=49|dock_size(1,11,0)=67|
""")), # noqa
('melodic',
textwrap.dedent("""
fsleyes.views.lightboxpanel.LightBoxPanel,fsleyes.views.timeseriespanel.TimeSeriesPanel,fsleyes.views.powerspectrumpanel.PowerSpectrumPanel
layout2|name=LightBoxPanel 1;caption=Lightbox View 1;state=67377088;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=TimeSeriesPanel 2;caption=Time series 2;state=67377148;dir=3;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=PowerSpectrumPanel 3;caption=Power spectra 3;state=67377148;dir=3;layer=0;row=0;pos=1;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|dock_size(3,0,0)=224|
fsleyes.controls.locationpanel.LocationPanel,fsleyes.controls.overlaylistpanel.OverlayListPanel,fsleyes.plugins.controls.melodicclassificationpanel.MelodicClassificationPanel,fsleyes.controls.lightboxtoolbar.LightBoxToolBar,fsleyes.controls.overlaydisplaytoolbar.OverlayDisplayToolBar;syncLocation=True,syncOverlayOrder=True,movieRate=750,syncOverlayDisplay=True;bgColour=#000000ff,fgColour=#ffffffff,showCursor=True,cursorColour=#00ff00ff,highlightSlice=False,zax=2,showColourBar=False,showGridLines=False,colourBarLocation=top
layout2|name=Panel;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=LocationPanel;caption=Location;state=67373052;dir=3;layer=0;row=0;pos=1;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OverlayListPanel;caption=Overlay list;state=67373052;dir=3;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=MelodicClassificationPanel;caption=Melodic IC classification;state=67373052;dir=2;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=LightBoxToolBar;caption=Lightbox view toolbar;state=67382012;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OverlayDisplayToolBar;caption=Display toolbar;state=67382012;dir=1;layer=11;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|dock_size(3,0,0)=130|dock_size(1,10,0)=45|dock_size(1,11,0)=51|dock_size(2,0,0)=402|
TimeSeriesToolBar;;
layout2|name=FigureCanvasWxAgg;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=TimeSeriesToolBar;caption=Time series toolbar;state=67382012;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=642|dock_size(1,10,0)=36|
PowerSpectrumToolBar;;
layout2|name=FigureCanvasWxAgg;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=PowerSpectrumToolBar;caption=Plot toolbar;state=67382012;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=642|dock_size(1,10,0)=36|
""")), # noqa
('feat',
textwrap.dedent("""
fsleyes.views.orthopanel.OrthoPanel,fsleyes.views.timeseriespanel.TimeSeriesPanel
layout2|name=OrthoPanel 1;caption=Ortho View 1;state=67377088;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=TimeSeriesPanel 2;caption=Time series 2;state=67377148;dir=3;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|dock_size(3,0,0)=282|
fsleyes.controls.overlaylistpanel.OverlayListPanel,fsleyes.controls.overlaydisplaytoolbar.OverlayDisplayToolBar,fsleyes.controls.orthotoolbar.OrthoToolBar,fsleyes.controls.locationpanel.LocationPanel,fsleyes.plugins.controls.clusterpanel.ClusterPanel;syncLocation=True,syncOverlayOrder=True,movieRate=750,syncOverlayDisplay=True;layout=horizontal,showLabels=True,bgColour=#000000ff,fgColour=#ffffffff,showCursor=True,showZCanvas=True,cursorColour=#00ff00ff,showColourBar=False,showYCanvas=True,showXCanvas=True,colourBarLocation=top
layout2|name=Panel;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OverlayListPanel;caption=Overlay list;state=67373052;dir=3;layer=2;row=0;pos=0;prop=87792;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OverlayDisplayToolBar;caption=Display toolbar;state=67382012;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OrthoToolBar;caption=Ortho view toolbar;state=67382012;dir=1;layer=10;row=1;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=LocationPanel;caption=Location;state=67373052;dir=3;layer=2;row=0;pos=1;prop=98544;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=ClusterPanel;caption=Cluster browser;state=67373052;dir=2;layer=1;row=0;pos=0;prop=114760;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=10|dock_size(2,1,0)=566|dock_size(1,10,0)=51|dock_size(1,10,1)=36|dock_size(3,2,0)=130|
OverlayListPanel,TimeSeriesToolBar;;
layout2|name=FigureCanvasWxAgg;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=OverlayListPanel;caption=Overlay list;state=67373052;dir=4;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|name=TimeSeriesToolBar;caption=Time series toolbar;state=67382012;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=642|dock_size(1,10,0)=36|dock_size(4,0,0)=206|
""")), # noqa
('ortho',
textwrap.dedent("""
fsleyes.views.orthopanel.OrthoPanel
layout2|name=OrthoPanel 1;caption=Ortho View 1;state=67376064;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|
;syncLocation=True,syncOverlayOrder=True,syncOverlayDisplay=True;layout=horizontal,showLabels=True,bgColour=#000000ff,fgColour=#ffffffff,showCursor=True,showZCanvas=True,cursorColour=#00ff00ff,showColourBar=False,showYCanvas=True,showXCanvas=True,colourBarLocation=top
layout2|name=Panel;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|
""")), # noqa
('3d',
textwrap.dedent("""
fsleyes.views.scene3dpanel.Scene3DPanel
layout2|name=Scene3DPanel 1;caption=3D View 1;state=67376064;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=24|
;syncOverlayOrder=True,syncOverlayDisplay=True,syncLocation=True;showColourBar=False,showLegend=True,cursorColour=#00ff00ff,colourBarLocation=top,showCursor=True,colourBarLabelSide=top-left,bgColour=#9999c0ff,fgColour=#00ff00ff
layout2|name=Panel;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|
""")), # noqa
('lightbox',
textwrap.dedent("""
fsleyes.views.lightboxpanel.LightBoxPanel
layout2|name=LightBoxPanel 1;caption=Lightbox View 1;state=67376064;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=22|
;syncLocation=True,syncOverlayOrder=True,syncOverlayDisplay=True;bgColour=#000000ff,fgColour=#ffffffff,showCursor=True,cursorColour=#00ff00ff,highlightSlice=False,zax=2,showColourBar=False,showGridLines=False,colourBarLocation=top
layout2|name=Panel;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=-1;besth=-1;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1;notebookid=-1;transparent=255|dock_size(5,0,0)=10|
""")))) # noqa