Skip to content
Snippets Groups Projects
Commit 9e069993 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Documentation/comments/refactoring for CanvasSettingsPanel,

ClusterPanel, HistogramControlPanel, HistogramListPanel, and
TimeSeriesListPanel.
parent 6f026f77
No related branches found
No related tags found
No related merge requests found
doc/images/canvassettingspanel.png

118 KiB

doc/images/clusterpanel.png

74.6 KiB

doc/images/histogramcontrolpanel.png

77.7 KiB

doc/images/histogramlistpanel.png

31.3 KiB

doc/images/timeserieslistpanel.png

49.1 KiB

#!/usr/bin/env python #!/usr/bin/env python
# #
# canvassettingspanel.py - # canvassettingspanel.py - The CanvasSettingsPanel class.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides the :class:`CanvasSettingsPanel` class, a *FSLeyes
control* panel which displays settings for a :class:`.CanvasPanel`.
"""
import wx import wx
...@@ -16,59 +20,55 @@ import fsl.fsleyes.panel as fslpanel ...@@ -16,59 +20,55 @@ import fsl.fsleyes.panel as fslpanel
import fsl.fsleyes.tooltips as fsltooltips import fsl.fsleyes.tooltips as fsltooltips
_CANVASPANEL_PROPS = [ class CanvasSettingsPanel(fslpanel.FSLEyesPanel):
props.Widget('syncOverlayOrder'), """The ``CanvasSettingsPanel`` is a *FSLeyes control* which displays
props.Widget('syncLocation'), settings for a :class:`.CanvasPanel` instance. A ``CanvasSettingsPanel``
props.Widget('syncOverlayDisplay'), looks something like this:
props.Widget('movieMode'),
props.Widget('movieRate', spin=False, showLimits=False),
]
_SCENEOPTS_PROPS = [ .. image:: images/canvassettingspanel.png
props.Widget('showCursor'), :scale: 50%
props.Widget('bgColour'), :align: center
props.Widget('cursorColour'),
props.Widget('performance',
spin=False,
showLimits=False,
labels=strings.choices['SceneOpts.performance']),
props.Widget('showColourBar'),
props.Widget('colourBarLabelSide',
labels=strings.choices['SceneOpts.colourBarLabelSide'],
enabledWhen=lambda o: o.showColourBar),
props.Widget('colourBarLocation',
labels=strings.choices['SceneOpts.colourBarLocation'],
enabledWhen=lambda o: o.showColourBar)
]
_ORTHOOPTS_PROPS = [
props.Widget('layout', labels=strings.choices['OrthoOpts.layout']),
props.Widget('zoom', spin=False, showLimits=False),
props.Widget('showLabels'),
props.Widget('showXCanvas'),
props.Widget('showYCanvas'),
props.Widget('showZCanvas')
]
_LIGHTBOXOPTS_PROPS = [ The ``CanvasSettingsPanel`` displays controls which modify properties on
props.Widget('zax', labels=strings.choices['CanvasOpts.zax']), the following classes:
props.Widget('zoom', showLimits=False, spin=False),
props.Widget('sliceSpacing', showLimits=False),
props.Widget('zrange', showLimits=False),
props.Widget('highlightSlice'),
props.Widget('showGridLines')
]
.. autosummary::
:nosignatures:
class CanvasSettingsPanel(fslpanel.FSLEyesPanel): ~fsl.fsleyes.views.CanvasPanel
~fsl.fsleyes.displaycontext.SceneOpts
~fsl.fsleyes.displaycontext.OrthoOpts
~fsl.fsleyes.displaycontext.LightBoxOpts
def __init__(self, parent, overlayList, displayCtx, canvasPanel): The ``CanvasSettingsPanel`` divides the displayed settings into those
which are common to all :class:`.CanvasPanel` instances, and those which
are specific to the :class:`.CanvasPanel` sub-class (i.e.
:class:`.OrthoPanel` or :class:`.LightBoxPanel`). The specific settings
that are displayed are defined in the following lists:
.. autosummary::
_CANVASPANEL_PROPS
_SCENEOPTS_PROPS
_ORTHOOPTS_PROPS
_LIGHTBOXOPTS_PROPS
"""
def __init__(self, parent, overlayList, displayCtx, canvasPanel):
"""Create a ``CanvasSettingsPanel``.
:arg parent: The :mod:`wx` parent object
:arg overlayList: The :class:`.OverlayList` instance.
:arg displayCtx: The :class:`.DisplayContext` instance.
:arg canvasPanel: The :class:`.CanvasPanel` instance.
"""
fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
self.__widgets = widgetlist.WidgetList(self) self.__widgets = widgetlist.WidgetList(self)
self.__sizer = wx.BoxSizer(wx.VERTICAL)
self.__sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.__sizer) self.SetSizer(self.__sizer)
...@@ -133,3 +133,62 @@ class CanvasSettingsPanel(fslpanel.FSLEyesPanel): ...@@ -133,3 +133,62 @@ class CanvasSettingsPanel(fslpanel.FSLEyesPanel):
self.__widgets.Expand(panelGroup) self.__widgets.Expand(panelGroup)
self.SetMinSize((21, 21)) self.SetMinSize((21, 21))
_CANVASPANEL_PROPS = [
props.Widget('syncOverlayOrder'),
props.Widget('syncLocation'),
props.Widget('syncOverlayDisplay'),
props.Widget('movieMode'),
props.Widget('movieRate', spin=False, showLimits=False),
]
"""A list of :class:`props.Widget` items defining controls to
display for :class:`.CanvasPanel` properties.
"""
_SCENEOPTS_PROPS = [
props.Widget('showCursor'),
props.Widget('bgColour'),
props.Widget('cursorColour'),
props.Widget('performance',
spin=False,
showLimits=False,
labels=strings.choices['SceneOpts.performance']),
props.Widget('showColourBar'),
props.Widget('colourBarLabelSide',
labels=strings.choices['SceneOpts.colourBarLabelSide'],
enabledWhen=lambda o: o.showColourBar),
props.Widget('colourBarLocation',
labels=strings.choices['SceneOpts.colourBarLocation'],
enabledWhen=lambda o: o.showColourBar)
]
"""A list of :class:`props.Widget` items defining controls to
display for :class:`.SceneOpts` properties.
"""
_ORTHOOPTS_PROPS = [
props.Widget('layout', labels=strings.choices['OrthoOpts.layout']),
props.Widget('zoom', spin=False, showLimits=False),
props.Widget('showLabels'),
props.Widget('showXCanvas'),
props.Widget('showYCanvas'),
props.Widget('showZCanvas')
]
"""A list of :class:`props.Widget` items defining controls to
display for :class:`.OrthoOpts` properties.
"""
_LIGHTBOXOPTS_PROPS = [
props.Widget('zax', labels=strings.choices['CanvasOpts.zax']),
props.Widget('zoom', showLimits=False, spin=False),
props.Widget('sliceSpacing', showLimits=False),
props.Widget('zrange', showLimits=False),
props.Widget('highlightSlice'),
props.Widget('showGridLines')
]
"""A list of :class:`props.Widget` items defining controls to
display for :class:`.LightBoxOpts` properties.
"""
#!/usr/bin/env python #!/usr/bin/env python
# #
# clusterpanel.py - # clusterpanel.py - The ClusterPanel class.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides the :class:`ClusterPanel` class, a *FSLeyes control*
panel for viewing cluster results from a FEAT analysis.
"""
import logging import logging
import wx import wx
...@@ -11,7 +14,6 @@ import wx ...@@ -11,7 +14,6 @@ import wx
import pwidgets.widgetgrid as widgetgrid import pwidgets.widgetgrid as widgetgrid
import fsl.fsleyes.panel as fslpanel import fsl.fsleyes.panel as fslpanel
import fsl.utils.transform as transform
import fsl.utils.dialog as fsldlg import fsl.utils.dialog as fsldlg
import fsl.data.strings as strings import fsl.data.strings as strings
import fsl.data.featimage as featimage import fsl.data.featimage as featimage
...@@ -21,8 +23,35 @@ log = logging.getLogger(__name__) ...@@ -21,8 +23,35 @@ log = logging.getLogger(__name__)
class ClusterPanel(fslpanel.FSLEyesPanel): class ClusterPanel(fslpanel.FSLEyesPanel):
"""The ``ClusterPanel`` shows a table of cluster results from the analysis
associated with a :class:`.FEATImage` overlay. A ``ClusterPanel`` looks
something like the following:
.. image:: images/clusterpanel.png
:scale: 50%
:align: center
The ``ClusterPanel`` contains controls which allow the user to:
- Select the COPE for which cluster results are displayed
- Add a Z statistic overlay for the currently displayed COPE
- Add a cluster mask overlay for the currently displayed COPE
- Navigate to the Z maximum location, Z centre-of-gravity location,
or COPE maximum location, for a specific cluster.
"""
def __init__(self, parent, overlayList, displayCtx): def __init__(self, parent, overlayList, displayCtx):
"""Create a ``ClusterPanel``.
:arg parent: The :mod:`wx` parent object.
:arg overlayList: The :class:`.OverlayList` instance.
:arg displayCtx: The :class:`.DisplayContext` instance.
"""
fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
self.__disabledText = wx.StaticText( self.__disabledText = wx.StaticText(
...@@ -80,9 +109,19 @@ class ClusterPanel(fslpanel.FSLEyesPanel): ...@@ -80,9 +109,19 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
self.__selectedOverlayChanged() self.__selectedOverlayChanged()
def destroy(self):
"""Must be called when this ``ClusterPanel`` is no longer needed.
Removes some property listeners, and calls
:meth:`.FSLEyesPanel.destroy`.
"""
self._overlayList.removeListener('overlays', self._name)
self._displayCtx .removeListener('selectedOverlay', self._name)
fslpanel.FSLEyesPanel.destroy(self)
def __calcMinSize(self): def __calcMinSize(self):
"""Figures out the minimum size that this ``ClusterPanel`` should """Figures out the minimum size that this ``ClusterPanel`` should
have. have. Called by :meth:`__init__`.
When the ``ClusterPanel`` is created, the COPE combo box is not When the ``ClusterPanel`` is created, the COPE combo box is not
populated, so has no minimum size. Here, we figure out a good minimum populated, so has no minimum size. Here, we figure out a good minimum
...@@ -101,22 +140,36 @@ class ClusterPanel(fslpanel.FSLEyesPanel): ...@@ -101,22 +140,36 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
return self.__sizer.GetMinSize() return self.__sizer.GetMinSize()
def destroy(self):
self._overlayList.removeListener('overlays', self._name)
self._displayCtx .removeListener('selectedOverlay', self ._name)
fslpanel.FSLEyesPanel.destroy(self)
def __disable(self, message): def __disable(self, message):
"""Disables the ``ClusterPanel``, and displays the given message.
Called when the selected overlay is not a :class:`.FEATImage`, or
when cluster results cannot be displayed for some reason.
"""
self.__disabledText.SetLabel(message) self.__disabledText.SetLabel(message)
self.__sizer.Show(self.__disabledText, True) self.__sizer.Show(self.__disabledText, True)
self.__sizer.Show(self.__mainSizer, False) self.__sizer.Show(self.__mainSizer, False)
self.Layout() self.Layout()
def __statSelected(self, ev):
"""Called when a COPE is selected. Clears the cluster table, and
displays clusters for the newly selected COPE (see the
:meth:`__displayClusterData` method)
"""
idx = self.__statSelect.GetSelection()
data = self.__statSelect.GetClientData(idx)
self.__displayClusterData(data)
self.__enableOverlayButtons()
def __addZStatsClick(self, ev): def __addZStatsClick(self, ev):
"""Called when the *Add Z statistics* button is pushed. Retrieves
the Z statistic image for the current COPE (see the
:meth:`.FEATImage.getZStats` method), and adds it as an overlay
to the :class:`.OverlayList`.
"""
overlay = self.__selectedOverlay overlay = self.__selectedOverlay
contrast = self.__statSelect.GetSelection() contrast = self.__statSelect.GetSelection()
...@@ -147,6 +200,11 @@ class ClusterPanel(fslpanel.FSLEyesPanel): ...@@ -147,6 +200,11 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
def __addClustMaskClick(self, ev): def __addClustMaskClick(self, ev):
"""Called when the *Add cluster mask* button is pushed. Retrieves the
cluster mask image for the currewnt contrast (see the
:meth:`.FEATImage.getClusterMask` method)
"""
overlay = self.__selectedOverlay overlay = self.__selectedOverlay
contrast = self.__statSelect.GetSelection() contrast = self.__statSelect.GetSelection()
mask = overlay.getClusterMask(contrast) mask = overlay.getClusterMask(contrast)
...@@ -161,13 +219,102 @@ class ClusterPanel(fslpanel.FSLEyesPanel): ...@@ -161,13 +219,102 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
self._overlayList.append(mask) self._overlayList.append(mask)
self._displayCtx.getDisplay(mask).overlayType = 'label' self._displayCtx.getDisplay(mask).overlayType = 'label'
def __displayClusterData(self, clusters):
"""Updates the cluster table so that it is displaying the given list
of clusters.
:arg clusters: A sequence of objects, each representing one cluster.
See the :meth:`.FEATImage.clusterResults` method.
"""
cols = {'index' : 0,
'nvoxels' : 1,
'p' : 2,
'logp' : 3,
'zmax' : 4,
'zmaxcoords' : 5,
'zcogcoords' : 6,
'copemax' : 7,
'copemaxcoords' : 8,
'copemean' : 9}
grid = self.__clusterList
overlay = self.__selectedOverlay
opts = self._displayCtx.getOpts(overlay)
grid.ClearGrid()
grid.SetGridSize(len(clusters), 10)
for col, i in cols.items():
grid.SetColLabel(i, strings.labels[self, col])
def makeCoordButton(coords):
label = wx.StaticText(grid, label='[{} {} {}]'.format(*coords))
btn = wx.Button(grid, label=u'\u2192', style=wx.BU_EXACTFIT)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(label, flag=wx.EXPAND, proportion=1)
sizer.Add(btn)
def onClick(ev):
dloc = opts.transformCoords([coords], 'voxel', 'display')[0]
self._displayCtx.location = dloc
btn.Bind(wx.EVT_BUTTON, onClick)
return sizer
dlg = fsldlg.SimpleMessageDialog()
dlg.Show()
for i, clust in enumerate(clusters):
dlg.SetMessage(
strings.messages[self, 'loadingCluster'].format(clust.index))
zmaxbtn = makeCoordButton((clust.zmaxx,
clust.zmaxy,
clust.zmaxz))
zcogbtn = makeCoordButton((clust.zcogx,
clust.zcogy,
clust.zcogz))
copemaxbtn = makeCoordButton((clust.copemaxx,
clust.copemaxy,
clust.copemaxz))
fmt = lambda v: '{}'.format(v)
grid.SetText( i, cols['index'], fmt(clust.index))
grid.SetText( i, cols['nvoxels'], fmt(clust.nvoxels))
grid.SetText( i, cols['p'], fmt(clust.p))
grid.SetText( i, cols['logp'], fmt(clust.logp))
grid.SetText( i, cols['zmax'], fmt(clust.zmax))
grid.SetWidget(i, cols['zmaxcoords'], zmaxbtn)
grid.SetWidget(i, cols['zcogcoords'], zcogbtn)
grid.SetText( i, cols['copemax'], fmt(clust.copemax))
grid.SetWidget(i, cols['copemaxcoords'], copemaxbtn)
grid.SetText( i, cols['copemean'], fmt(clust.copemean))
dlg.Close()
dlg.Destroy()
def __overlayListChanged(self, *a): def __overlayListChanged(self, *a):
"""Called when the :class:`.OverlayList` changes. Updates the *Add Z
statistic* and *Add cluster mask* buttons, in case the user removed
them. Also calls :meth:`__selectedOverlayChanged`.
"""
self.__selectedOverlayChanged() self.__selectedOverlayChanged()
self.__enableOverlayButtons() self.__enableOverlayButtons()
def __enableOverlayButtons(self): def __enableOverlayButtons(self):
"""Enables/disables the *Add Z statistic* and *Add cluster mask*
buttons depending on whether the corresponding overlays are in the
:class:`.OverlayList`.
"""
if self.__selectedOverlay is None: if self.__selectedOverlay is None:
return return
...@@ -185,6 +332,13 @@ class ClusterPanel(fslpanel.FSLEyesPanel): ...@@ -185,6 +332,13 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
def __selectedOverlayChanged(self, *a): def __selectedOverlayChanged(self, *a):
"""Called when the :attr:`.DisplayContext.selectedOverlay` changes,
and by the :meth:`__overlayListChanged` method.
If the newly selected overlay is a :class:`.FEATImage` which has
cluster results, they are loaded in, and displayed on the cluster
table.
"""
prevOverlay = self.__selectedOverlay prevOverlay = self.__selectedOverlay
self.__selectedOverlay = None self.__selectedOverlay = None
...@@ -255,85 +409,3 @@ class ClusterPanel(fslpanel.FSLEyesPanel): ...@@ -255,85 +409,3 @@ class ClusterPanel(fslpanel.FSLEyesPanel):
self.Layout() self.Layout()
return return
def __statSelected(self, ev):
idx = self.__statSelect.GetSelection()
data = self.__statSelect.GetClientData(idx)
self.__displayClusterData(data)
self.__enableOverlayButtons()
def __displayClusterData(self, clusters):
cols = {'index' : 0,
'nvoxels' : 1,
'p' : 2,
'logp' : 3,
'zmax' : 4,
'zmaxcoords' : 5,
'zcogcoords' : 6,
'copemax' : 7,
'copemaxcoords' : 8,
'copemean' : 9}
grid = self.__clusterList
overlay = self.__selectedOverlay
opts = self._displayCtx.getOpts(overlay)
grid.ClearGrid()
grid.SetGridSize(len(clusters), 10)
for col, i in cols.items():
grid.SetColLabel(i, strings.labels[self, col])
def makeCoordButton(coords):
label = wx.StaticText(grid, label='[{} {} {}]'.format(*coords))
btn = wx.Button(grid, label=u'\u2192', style=wx.BU_EXACTFIT)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(label, flag=wx.EXPAND, proportion=1)
sizer.Add(btn)
def onClick(ev):
dloc = opts.transformCoords([coords], 'voxel', 'display')[0]
self._displayCtx.location = dloc
btn.Bind(wx.EVT_BUTTON, onClick)
return sizer
dlg = fsldlg.SimpleMessageDialog()
dlg.Show()
for i, clust in enumerate(clusters):
dlg.SetMessage(
strings.messages[self, 'loadingCluster'].format(clust.index))
zmaxbtn = makeCoordButton((clust.zmaxx,
clust.zmaxy,
clust.zmaxz))
zcogbtn = makeCoordButton((clust.zcogx,
clust.zcogy,
clust.zcogz))
copemaxbtn = makeCoordButton((clust.copemaxx,
clust.copemaxy,
clust.copemaxz))
fmt = lambda v: '{}'.format(v)
grid.SetText( i, cols['index'], fmt(clust.index))
grid.SetText( i, cols['nvoxels'], fmt(clust.nvoxels))
grid.SetText( i, cols['p'], fmt(clust.p))
grid.SetText( i, cols['logp'], fmt(clust.logp))
grid.SetText( i, cols['zmax'], fmt(clust.zmax))
grid.SetWidget(i, cols['zmaxcoords'], zmaxbtn)
grid.SetWidget(i, cols['zcogcoords'], zcogbtn)
grid.SetText( i, cols['copemax'], fmt(clust.copemax))
grid.SetWidget(i, cols['copemaxcoords'], copemaxbtn)
grid.SetText( i, cols['copemean'], fmt(clust.copemean))
dlg.Close()
dlg.Destroy()
#!/usr/bin/env python #!/usr/bin/env python
# #
# histogramcontrolpanel.py - # histogramcontrolpanel.py - The HistogramControlPanel class.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides the :class:`HistogramControlPanel` class, a *FSLeyes
control* panel which allows a :class:`.HistogramPanel` to be configured.
"""
import wx import wx
...@@ -16,8 +20,40 @@ import fsl.data.strings as strings ...@@ -16,8 +20,40 @@ import fsl.data.strings as strings
class HistogramControlPanel(fslpanel.FSLEyesPanel): class HistogramControlPanel(fslpanel.FSLEyesPanel):
"""The ``HistogramControlPanel`` is a *FSLeyes control* panel which
allows the user to configure a :class:`.HistogramPanel`. A
``HistogramControlPanel`` looks something like the following:
.. image:: images/histogramcontrolpanel.png
:scale: 50%
:align: center
The ``HistogramControlPanel`` divides its settings into three main
sections:
- The *Histogram settings* section contains controls which modify
properties defined in the :class:`.HistogramPanel`.
- *General plot settings* section contains controls which modify
properties defined in the :class:`.PlotPanel`.
- The *Settings for the current histogram plot* section contains
controls which modify properties defined in the
:class:`.HistogramSeries` class, and affect the currently
selected plot in the :class:`.HistogramListPanel`. If no plots
have been added to the histogram list, this section is hidden.
"""
def __init__(self, parent, overlayList, displayCtx, hsPanel): def __init__(self, parent, overlayList, displayCtx, hsPanel):
"""Create a ``HistogramControlPanel``.
:arg parent: The :mod:`wx` parent object.
:arg overlayList: The :class:`.OverlayList` instance.
:arg displayCtx: The :class:`.DisplayContext` instance.
:arg hsPanel: The :class:`.HistogramPanel` instance.
"""
fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
...@@ -127,6 +163,11 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel): ...@@ -127,6 +163,11 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
def destroy(self): def destroy(self):
"""Must be called when this ``HistogramControlPanel`` is no longer
needed. Removes some property listeners, and calls the
:meth:`.FSLEyesPanel.destroy` method.
"""
self.__hsPanel.removeListener('selectedSeries', self._name) self.__hsPanel.removeListener('selectedSeries', self._name)
self.__hsPanel.removeListener('dataSeries', self._name) self.__hsPanel.removeListener('dataSeries', self._name)
if self.__currentHs is not None: if self.__currentHs is not None:
...@@ -136,6 +177,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel): ...@@ -136,6 +177,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
def __selectedSeriesChanged(self, *a): def __selectedSeriesChanged(self, *a):
"""Called when the :attr:`.HistogramPanel.selectedSeries` property
changes. Updates the section containing settings for the current
histogram plot (see the :meth:`__updateCurrentProperties` method).
"""
panel = self.__hsPanel panel = self.__hsPanel
...@@ -149,6 +194,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel): ...@@ -149,6 +194,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
def __hsLabelChanged(self, *a): def __hsLabelChanged(self, *a):
"""Called when the :attr:`.DataSeries.label` property, on the currently
selected :class:`.HistogramSeries`, changes. Updates the label for the
relevant settings section.
"""
if self.__currentHs is None: if self.__currentHs is None:
return return
...@@ -159,6 +208,9 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel): ...@@ -159,6 +208,9 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
def __updateCurrentProperties(self): def __updateCurrentProperties(self):
"""Updates the settings shown in the section for the current histogram
plot.
"""
expanded = False expanded = False
scrollPos = self.__widgets.GetViewStart() scrollPos = self.__widgets.GetViewStart()
...@@ -185,7 +237,7 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel): ...@@ -185,7 +237,7 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
self.__nbins = props.makeWidget(wlist, hs, 'nbins', showLimits=False) self.__nbins = props.makeWidget(wlist, hs, 'nbins', showLimits=False)
volume = props.makeWidget(wlist, hs, 'volume', showLimits=False) volume = props.makeWidget(wlist, hs, 'volume', showLimits=False)
dataRange = props.makeWidget( dataRange = props.makeWidget(
wlist, wlist,
hs, hs,
...@@ -233,6 +285,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel): ...@@ -233,6 +285,10 @@ class HistogramControlPanel(fslpanel.FSLEyesPanel):
def __autoBinChanged(self, *a): def __autoBinChanged(self, *a):
"""Called when the :attr:`.HistogramPanel.autoBin` setting changes. If
necessary, enables/disables the control which is bound to the
:attr:`.HistogramSeries.nbins` property for the current histogram plot.
"""
if self.__currentHs is None or self.__nbins is None: if self.__currentHs is None or self.__nbins is None:
return return
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# histogramlistpanel.py - # histogramlistpanel.py - The HistogramListPanel class.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides the :class:`HistogramListPanel` class, which
is a *FSLeyes control* panel for use with :class:`.HistogramPanel` views.
"""
import wx import wx
...@@ -16,8 +19,34 @@ import timeserieslistpanel ...@@ -16,8 +19,34 @@ import timeserieslistpanel
class HistogramListPanel(fslpanel.FSLEyesPanel): class HistogramListPanel(fslpanel.FSLEyesPanel):
"""The ``HistogramListPanel`` is a control panel for use with the
:class:`.HistogramPanel` view. It allows the user to add/remove
:class:`.HistogramSeries` plots to/from the :class:`.HistogramPanel`,
and to configure the display settings for each. A ``HistogramListPanel``
looks something like this:
.. image:: images/histogramlistpanel.png
:scale: 50%
:align: center
The ``HistogramListPanel`` performs the same task for the
:class:`.HistogramPanel` that the :class:`.TimeSeriesListPanel` does for
the :class:`.TimeSeriesPanel`. For each :class:`.HistogramSeries` that is
in the :attr:`.PlotPanel.dataSeries` list of the :class:`.HistogramPanel`,
a :class:`.TimeSeriesWidget` control is added to a
:class:`pwidgets.EditableListBox`.
"""
def __init__(self, parent, overlayList, displayCtx, histPanel): def __init__(self, parent, overlayList, displayCtx, histPanel):
"""Create a ``HistogramListPanel``.
:arg parent: The :mod:`wx` parent object.
:arg overlayList: The :class:`.OverlayList` instance.
:arg displayCtx: The :class:`.DisplayContext` instance.
:arg histPanel: The :class:`.HistogramPanel` instance.
"""
fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
...@@ -45,15 +74,26 @@ class HistogramListPanel(fslpanel.FSLEyesPanel): ...@@ -45,15 +74,26 @@ class HistogramListPanel(fslpanel.FSLEyesPanel):
def destroy(self): def destroy(self):
"""Must be called when this ``HistogramListPanel`` is no longer needed.
Removes some property listeners, and calls the
:meth:`.FSLEyesPanel.destroy` method.
"""
self.__hsPanel.removeListener('dataSeries', self._name) self.__hsPanel.removeListener('dataSeries', self._name)
fslpanel.FSLEyesPanel.destroy(self) fslpanel.FSLEyesPanel.destroy(self)
def getListBox(self): def getListBox(self):
"""Returns the :class:`pwidgets.EditableListBox` instance contained
within this ``HistogramListPanel``.
"""
return self.__hsList return self.__hsList
def __histSeriesChanged(self, *a): def __histSeriesChanged(self, *a):
"""Called when the :attr:`.PlotPanel.dataSeries` list of the
:class:`.HistogramPanel` changes. Refreshes the contents of the
:class:`pwidgets.EditableListBox`.
"""
self.__hsList.Clear() self.__hsList.Clear()
...@@ -69,6 +109,10 @@ class HistogramListPanel(fslpanel.FSLEyesPanel): ...@@ -69,6 +109,10 @@ class HistogramListPanel(fslpanel.FSLEyesPanel):
def __onListAdd(self, ev): def __onListAdd(self, ev):
"""Called when the user pushes the *add* button on the
:class:`pwidgets.EditableListBox`. Adds the *current* histogram plot
to the list (see the :class:`.HistogramPanel` class documentation).
"""
hs = self.__hsPanel.getCurrent() hs = self.__hsPanel.getCurrent()
if hs is None: if hs is None:
...@@ -85,16 +129,31 @@ class HistogramListPanel(fslpanel.FSLEyesPanel): ...@@ -85,16 +129,31 @@ class HistogramListPanel(fslpanel.FSLEyesPanel):
def __onListEdit(self, ev): def __onListEdit(self, ev):
"""Called when the user edits a label on the
:class:`pwidgets.EditableListBox`. Updates the
:attr:`.DataSeries.label` property of the corresponding
:class:`.HistogramSeries` instance.
"""
ev.data.label = ev.label ev.data.label = ev.label
def __onListSelect(self, ev): def __onListSelect(self, ev):
"""Called when the user selects an item in the
:class:`pwidgets.EditableListBox`. Sets the
:attr:`.DisplayContext.selectedOverlay` to the overlay associated with
the corresponding :class:`.HistogramSeries` instance.
"""
overlay = ev.data.overlay overlay = ev.data.overlay
self._displayCtx.selectedOverlay = self._overlayList.index(overlay) self._displayCtx.selectedOverlay = self._overlayList.index(overlay)
self.__hsPanel.selectedSeries = ev.idx self.__hsPanel.selectedSeries = ev.idx
def __onListRemove(self, ev): def __onListRemove(self, ev):
"""Called when the user removes an item from the
:class:`pwidgets.EditableListBox`. Removes the corresponding
:class:`.HistogramSeries` instance from the
:attr:`.PlotPanel.dataSeries` list of the :class:`.HistogramPanel`.
"""
self.__hsPanel.dataSeries.remove(ev.data) self.__hsPanel.dataSeries.remove(ev.data)
self.__hsPanel.selectedSeries = self.__hsList.GetSelection() self.__hsPanel.selectedSeries = self.__hsList.GetSelection()
ev.data.destroy() ev.data.destroy()
#!/usr/bin/env python #!/usr/bin/env python
# #
# timeserieslistpanel.py - # timeserieslistpanel.py - The TimeSeriesListPanel and TimeSeriesWidget
# classes.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides the :class:`TimeSeriesListPanel` class, which is a
*FSLeyes control* panel for use with the :class:`.TimeSeriesPanel` view.
"""
import copy import copy
...@@ -18,54 +23,42 @@ import fsl.data.strings as strings ...@@ -18,54 +23,42 @@ import fsl.data.strings as strings
import fsl.fsleyes.colourmaps as fslcm import fsl.fsleyes.colourmaps as fslcm
class TimeSeriesWidget(wx.Panel):
def __init__(self, parent, timeSeries):
wx.Panel.__init__(self, parent)
self.colour = props.makeWidget(self,
timeSeries,
'colour')
self.alpha = props.makeWidget(self,
timeSeries,
'alpha',
slider=True,
spin=False,
showLimits=False)
self.lineWidth = props.makeWidget(self,
timeSeries,
'lineWidth')
self.lineStyle = props.makeWidget(
self,
timeSeries,
'lineStyle',
labels=strings.choices['DataSeries.lineStyle'])
self.colour.SetToolTipString(
fsltooltips.properties[timeSeries, 'colour'])
self.alpha.SetToolTipString(
fsltooltips.properties[timeSeries, 'alpha'])
self.lineWidth.SetToolTipString(
fsltooltips.properties[timeSeries, 'lineWidth'])
self.lineStyle.SetToolTipString(
fsltooltips.properties[timeSeries, 'lineStyle'])
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.SetSizer(self.sizer)
self.sizer.Add(self.colour)
self.sizer.Add(self.alpha)
self.sizer.Add(self.lineWidth)
self.sizer.Add(self.lineStyle)
self.Layout()
class TimeSeriesListPanel(fslpanel.FSLEyesPanel): class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
"""The ``TimeSeriesListPanel`` is a control panel for use with the
:class:`.TimeSeriesPanel` view. It allows the user to add/remove
:class:`.TimeSeries` plots to/from the ``TimeSeriesPanel``, and to
configure the settings for each ``TimeSeries`` in the
:attr:`.PlotPanel.dataSeries` list. A ``TimeSeriesListPanel`` looks
something like the following:
.. image:: images/timeserieslistpanel.png
:scale: 50%
:align: center
For every :class:`.TimeSeries` instance in the
:attr:`.PlotPanel.dataSeries` list of the :class:`.TimeSeriesPanel`, the
``TimeSeriesListPanel`` creates a :class:`.TimeSeriesWidget`, which allows
the user to change the display settings of the :class:`.TimeSeries`
instance. A :class:`pwidgets.EditableListBox` is used to display the
labels for each :class:`.TimeSeries` instance, and the associated
:class:`.TimeSeriesWidget` controls.
A label is also shown above the list, containing the name of the currently
selected overlay, and the voxel coordinates of the current
:attr:`.DisplayContext.location`.
"""
def __init__(self, parent, overlayList, displayCtx, timeSeriesPanel): def __init__(self, parent, overlayList, displayCtx, timeSeriesPanel):
"""Create a ``TimeSeriesListPanel``.
:arg parent: The :mod:`wx` parent object.
:arg overlayList: The :class:`.OverlayList` instance.
:arg displayCtx: The :class:`.DisplayContext` instance.
:arg timeSeriesPanel: The :class:`.TimeSeriesPanel` instance.
"""
fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
self.__tsPanel = timeSeriesPanel self.__tsPanel = timeSeriesPanel
...@@ -104,6 +97,11 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel): ...@@ -104,6 +97,11 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
def destroy(self): def destroy(self):
"""Must be called when this ``TimeSeriesListPanel`` is no longer
needed. Removes some property listeners, and calls the
:meth:`.FSLEyesPanel.destroy` method.
"""
self._displayCtx .removeListener('selectedOverlay', self._name) self._displayCtx .removeListener('selectedOverlay', self._name)
self._displayCtx .removeListener('location', self._name) self._displayCtx .removeListener('location', self._name)
self._overlayList.removeListener('overlays', self._name) self._overlayList.removeListener('overlays', self._name)
...@@ -113,6 +111,8 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel): ...@@ -113,6 +111,8 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
def __makeLabel(self, ts): def __makeLabel(self, ts):
"""Creates a label to use for the given :class:`.TimeSeries` instance.
"""
display = self._displayCtx.getDisplay(ts.overlay) display = self._displayCtx.getDisplay(ts.overlay)
...@@ -123,6 +123,9 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel): ...@@ -123,6 +123,9 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
def __makeFEATModelTSLabel(self, parentTs, modelTs): def __makeFEATModelTSLabel(self, parentTs, modelTs):
"""Creates a label to use for the given :class:`.FEATTimeSeries`
instance.
"""
import fsl.fsleyes.views.timeseriespanel as tsp import fsl.fsleyes.views.timeseriespanel as tsp
...@@ -157,6 +160,10 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel): ...@@ -157,6 +160,10 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
def __timeSeriesChanged(self, *a): def __timeSeriesChanged(self, *a):
"""Called when the :attr:`.PlotPanel.dataSeries` list of the
:class:`.TimeSeriesPanel` changes. Updates the list of
:class:`.TimeSeriesWidget` controls.
"""
self.__tsList.Clear() self.__tsList.Clear()
...@@ -170,17 +177,28 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel): ...@@ -170,17 +177,28 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
def __locationChanged(self, *a): def __locationChanged(self, *a):
"""Called when the :attr:`.DisplayContext.location` changes.
Updates a label at the top of the ``TimeSeriesListPanel``, displaying
the currently selected overlay, and the current voxel location.
"""
ts = self.__tsPanel.getCurrent() ts = self.__tsPanel.getCurrent()
if ts is None: if ts is None:
self.__currentLabel.SetLabel('') self.__currentLabel.SetLabel('')
return return
self.__currentLabel.SetLabel(self.__makeLabel(ts)) self.__currentLabel.SetLabel(self.__makeLabel(ts))
def __onListAdd(self, ev): def __onListAdd(self, ev):
"""Called when the user pushes the *add* button on the
:class:`pwidgets.EditableListBox`. Adds the *current* time series
plot to the :attr:`.PlotPanel.dataSeries` list of the
:class:`.TimeSeriesPanel` (see the :class:`.TimeSeriesPanel` class
documentation).
"""
import fsl.fsleyes.views.timeseriespanel as tsp import fsl.fsleyes.views.timeseriespanel as tsp
...@@ -215,10 +233,20 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel): ...@@ -215,10 +233,20 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
def __onListEdit(self, ev): def __onListEdit(self, ev):
"""Called when the user edits a label on the
:class:`pwidgets.EditableListBox`. Updates the
:attr:`.DataSeries.label` property of the corresponding
:class:`.TimeSeries` instance.
"""
ev.data.label = ev.label ev.data.label = ev.label
def __onListSelect(self, ev): def __onListSelect(self, ev):
"""Called when the user selects an item in the
:class:`pwidgets.EditableListBox`. Sets the
:attr:`.DisplayContext.selectedOverlay` to the overlay associated with
the corresponding :class:`.TimeSeries` instance.
"""
overlay = ev.data.overlay overlay = ev.data.overlay
coords = ev.data.coords coords = ev.data.coords
...@@ -231,4 +259,68 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel): ...@@ -231,4 +259,68 @@ class TimeSeriesListPanel(fslpanel.FSLEyesPanel):
def __onListRemove(self, ev): def __onListRemove(self, ev):
"""Called when the user removes an item from the
:class:`pwidgets.EditableListBox`. Removes the corresponding
:class:`.TimeSeries` instance from the :attr:`.PlotPanel.dataSeries`
list of the :class:`.TimeSeriesPanel`.
"""
self.__tsPanel.dataSeries.remove(ev.data) self.__tsPanel.dataSeries.remove(ev.data)
class TimeSeriesWidget(wx.Panel):
"""The ``TimeSeriesWidget`` class is a panel which contains controls
that modify the properties of a :class:`.TimeSeries` instance. A
``TimeSeriesWidget`` is created by the :class:`TimeSeriesListPanel` for
every ``TimeSeries`` in the :attr:`.TimeSeriesPanel.dataSeries` list.
A ``TimeSeriesWidget`` may be created for instances of any sub-class
of :class:`.DataSeries`, not just the :class:`.TimeSeries` class.
"""
def __init__(self, parent, timeSeries):
"""Create a ``TimeSeriesWidget``.
:arg parent: The :mod:`wx` parent object.
:arg timeSeries: The :class:`.DataSeries` instance.
"""
wx.Panel.__init__(self, parent)
self.__colour = props.makeWidget(self,
timeSeries,
'colour')
self.__alpha = props.makeWidget(self,
timeSeries,
'alpha',
slider=True,
spin=False,
showLimits=False)
self.__lineWidth = props.makeWidget(self,
timeSeries,
'lineWidth')
self.__lineStyle = props.makeWidget(
self,
timeSeries,
'lineStyle',
labels=strings.choices['DataSeries.lineStyle'])
self.__colour.SetToolTipString(
fsltooltips.properties[timeSeries, 'colour'])
self.__alpha.SetToolTipString(
fsltooltips.properties[timeSeries, 'alpha'])
self.__lineWidth.SetToolTipString(
fsltooltips.properties[timeSeries, 'lineWidth'])
self.__lineStyle.SetToolTipString(
fsltooltips.properties[timeSeries, 'lineStyle'])
self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
self.SetSizer(self.__sizer)
self.__sizer.Add(self.__colour)
self.__sizer.Add(self.__alpha)
self.__sizer.Add(self.__lineWidth)
self.__sizer.Add(self.__lineStyle)
self.Layout()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment