diff --git a/doc/images/overlaylistpanel.png b/doc/images/overlaylistpanel.png new file mode 100644 index 0000000000000000000000000000000000000000..74bf7d1dba485353fea4ba6873646b94c89d92db Binary files /dev/null and b/doc/images/overlaylistpanel.png differ diff --git a/doc/images/timeseriescontrolpanel.png b/doc/images/timeseriescontrolpanel.png new file mode 100644 index 0000000000000000000000000000000000000000..6876f4d8104e9be6f6ab1cb2edef2430e018838b Binary files /dev/null and b/doc/images/timeseriescontrolpanel.png differ diff --git a/fsl/fsleyes/controls/overlaylistpanel.py b/fsl/fsleyes/controls/overlaylistpanel.py index 405dca8f0e618e2f37039559cfc71c755af9f2f1..43706c090c1a5894fa3c650280f918743d8bccb4 100644 --- a/fsl/fsleyes/controls/overlaylistpanel.py +++ b/fsl/fsleyes/controls/overlaylistpanel.py @@ -1,14 +1,14 @@ #!/usr/bin/env python # -# overlaylistpanel.py - A panel which displays a list of overlays in the -# overlay list. +# overlaylistpanel.py - The OverlayListPanel. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -"""A panel which displays a list of overlays in the overlay list (see and -allows the user to add/remove overlays, and to change their order. +"""This module provides the ``OverlayListPanel``, a *FSLeyes control* which +displays a list of all overlays currently in the :class:`.OverlayList`. """ + import logging import wx @@ -25,135 +25,31 @@ import fsl.data.image as fslimage log = logging.getLogger(__name__) -class ListItemWidget(wx.Panel): - - _enabledFG = '#000000' - _disabledFG = '#CCCCCC' - - def __init__(self, parent, overlay, display, displayCtx, listBox): - wx.Panel.__init__(self, parent) - - self.overlay = overlay - self.display = display - self.displayCtx = displayCtx - self.listBox = listBox - self.name = '{}_{}'.format(self.__class__.__name__, id(self)) - - # BU_NOTEXT causes a segmentation fault under OSX - if wx.Platform == '__WXMAC__': btnStyle = wx.BU_EXACTFIT - else: btnStyle = wx.BU_EXACTFIT | wx.BU_NOTEXT - - self.saveButton = wx.Button( self, style=btnStyle) - self.lockButton = wx.ToggleButton(self, style=btnStyle) - - self.saveButton.SetBitmap(icons.loadBitmap('floppydisk16')) - self.lockButton.SetBitmap(icons.loadBitmap('chainlink16')) - - self.visibility = props.makeWidget( - self, - display, - 'enabled', - icon=icons.findImageFile('eye16')) - - self.sizer = wx.BoxSizer(wx.HORIZONTAL) - - self.SetSizer(self.sizer) - - self.sizer.Add(self.saveButton, flag=wx.EXPAND, proportion=1) - self.sizer.Add(self.lockButton, flag=wx.EXPAND, proportion=1) - self.sizer.Add(self.visibility, flag=wx.EXPAND, proportion=1) - - # There is currently only one overlay - # group in the application. In the - # future there may be multiple groups. - group = displayCtx.overlayGroups[0] - - display.addListener('enabled', self.name, self.__vizChanged) - group .addListener('overlays', self.name, self.__overlayGroupChanged) - - if isinstance(overlay, fslimage.Image): - overlay.addListener('saved', self.name, self.__saveStateChanged) - else: - self.saveButton.Enable(False) - - self.saveButton.Bind(wx.EVT_BUTTON, self.__onSaveButton) - self.lockButton.Bind(wx.EVT_TOGGLEBUTTON, self.__onLockButton) - self .Bind(wx.EVT_WINDOW_DESTROY, self.__onDestroy) - - self.__overlayGroupChanged() - self.__vizChanged() - self.__saveStateChanged() - - - def __overlayGroupChanged(self, *a): - - group = self.displayCtx.overlayGroups[0] - self.lockButton.SetValue(self.overlay in group.overlays) - - - def __onSaveButton(self, ev): - self.displayCtx.selectOverlay(self.overlay) - self.overlay.save() - - - def __onLockButton(self, ev): - self.displayCtx.selectOverlay(self.overlay) - group = self.displayCtx.overlayGroups[0] - - if self.lockButton.GetValue(): group.addOverlay( self.overlay) - else: group.removeOverlay(self.overlay) - - - def __onDestroy(self, ev): - ev.Skip() - if ev.GetEventObject() is not self: - return - - group = self.displayCtx.overlayGroups[0] - - self.display.removeListener('enabled', self.name) - group .removeListener('overlays', self.name) - - if isinstance(self.overlay, fslimage.Image): - self.overlay.removeListener('saved', self.name) - - - def __saveStateChanged(self, *a): - - if not isinstance(self.overlay, fslimage.Image): - return - - idx = self.listBox.IndexOf(self.overlay) - - self.saveButton.Enable(not self.overlay.saved) - - if self.overlay.saved: - self.listBox.SetItemBackgroundColour(idx) - else: - self.listBox.SetItemBackgroundColour(idx, '#ffaaaa', '#aa4444') - - - def __vizChanged(self, *a): - self.displayCtx.selectOverlay(self.overlay) - - idx = self.listBox.IndexOf(self.overlay) - - if self.display.enabled: fgColour = ListItemWidget._enabledFG - else: fgColour = ListItemWidget._disabledFG - - self.listBox.SetItemForegroundColour(idx, fgColour) +class OverlayListPanel(fslpanel.FSLEyesPanel): + """The ``OverlayListPanel`` displays all overlays in the + :class:`.OverlayList`, and allows the user to add, remove, and re-order + overlays. An ``OverlayListPanel`` looks something like this: + .. image:: images/overlaylistpanel.png + :scale: 50% + :align: center -class OverlayListPanel(fslpanel.FSLEyesPanel): - """A :class:`.ControlPanel` which contains an :class:`.EditableListBox` - displaying the list of loaded overlays. - The list box allows the overlay order to be changed, and allows overlays - to be added and removed from the list. + A :class:`ListItemWidget` is displayed alongside every overlay in the + list - this allows the user to enable/disable, group, and save each + overlay. + + The ``OverlayListPanel`` is closely coupled to a few + :class:`.DisplayContext` properties: the + :attr:`.DisplayContext.selectedOverlay` property is linked to the currently + selected item in the overlay list, and the order in which the overlays are + shown is defined by the :attr:`.DisplayContext.overlayOrder` property. This + property is updated when the user changes the order of items in the list. """ + def __init__(self, parent, overlayList, displayCtx): - """Create and lay out an :class:`OverlayListPanel`. + """Create an ``OverlayListPanel``. :param parent: The :mod:`wx` parent object. :param overlayList: An :class:`.OverlayList` instance. @@ -164,49 +60,52 @@ class OverlayListPanel(fslpanel.FSLEyesPanel): # list box containing the list of overlays - it # is populated in the _overlayListChanged method - self._listBox = elistbox.EditableListBox( + self.__listBox = elistbox.EditableListBox( self, style=(elistbox.ELB_REVERSE | elistbox.ELB_TOOLTIP)) # listeners for when the user does # something with the list box - self._listBox.Bind(elistbox.EVT_ELB_SELECT_EVENT, self._lbSelect) - self._listBox.Bind(elistbox.EVT_ELB_MOVE_EVENT, self._lbMove) - self._listBox.Bind(elistbox.EVT_ELB_REMOVE_EVENT, self._lbRemove) - self._listBox.Bind(elistbox.EVT_ELB_ADD_EVENT, self._lbAdd) - self._listBox.Bind(elistbox.EVT_ELB_DBLCLICK_EVENT, self._lbDblClick) + self.__listBox.Bind(elistbox.EVT_ELB_SELECT_EVENT, self.__lbSelect) + self.__listBox.Bind(elistbox.EVT_ELB_MOVE_EVENT, self.__lbMove) + self.__listBox.Bind(elistbox.EVT_ELB_REMOVE_EVENT, self.__lbRemove) + self.__listBox.Bind(elistbox.EVT_ELB_ADD_EVENT, self.__lbAdd) + self.__listBox.Bind(elistbox.EVT_ELB_DBLCLICK_EVENT, self.__lbDblClick) - self._sizer = wx.BoxSizer(wx.HORIZONTAL) - self.SetSizer(self._sizer) + self.__sizer = wx.BoxSizer(wx.HORIZONTAL) + self.SetSizer(self.__sizer) - self._sizer.Add(self._listBox, flag=wx.EXPAND, proportion=1) + self.__sizer.Add(self.__listBox, flag=wx.EXPAND, proportion=1) self._overlayList.addListener( 'overlays', self._name, - self._overlayListChanged) + self.__overlayListChanged) self._displayCtx.addListener( 'overlayOrder', self._name, - self._overlayListChanged) + self.__overlayListChanged) self._displayCtx.addListener( 'selectedOverlay', self._name, - self._selectedOverlayChanged) + self.__selectedOverlayChanged) - self._overlayListChanged() - self._selectedOverlayChanged() + self.__overlayListChanged() + self.__selectedOverlayChanged() self.Layout() - self.SetMinSize(self._sizer.GetMinSize()) + self.SetMinSize(self.__sizer.GetMinSize()) def destroy(self): - """Deregisters property listeners.""" + """Must be called when this ``OverlayListPanel`` 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) @@ -221,18 +120,21 @@ class OverlayListPanel(fslpanel.FSLEyesPanel): fslpanel.FSLEyesPanel.destroy(self) - def _selectedOverlayChanged(self, *a): + def __selectedOverlayChanged(self, *a): """Called when the :attr:`.DisplayContext.selectedOverlay` property changes. Updates the selected item in the list box. """ if len(self._overlayList) > 0: - self._listBox.SetSelection( + self.__listBox.SetSelection( self._displayCtx.getOverlayOrder( self._displayCtx.selectedOverlay)) - def _overlayNameChanged(self, value, valid, display, propName): + def __overlayNameChanged(self, value, valid, display, propName): + """Called when the :attr:`.Display.name` of an overlay changes. Updates + the corresponding label in the overlay list. + """ overlay = display.getOverlay() idx = self._displayCtx.getOverlayOrder(overlay) @@ -241,18 +143,15 @@ class OverlayListPanel(fslpanel.FSLEyesPanel): if name is None: name = '' - self._listBox.SetItemLabel(idx, name) + self.__listBox.SetItemLabel(idx, name) - def _overlayListChanged(self, *a): - """Called when the :class:`.OverlayList.overlays` list changes. - - If the change was due to user action on the :class:`.EditableListBox`, - this method does nothing. Otherwise, this method updates the - :class:`.EditableListBox` + def __overlayListChanged(self, *a): + """Called when the :class:`.OverlayList` changes. All of the items + in the overlay list are re-created. """ - self._listBox.Clear() + self.__listBox.Clear() for i, overlay in enumerate(self._displayCtx.getOrderedOverlays()): @@ -262,39 +161,40 @@ class OverlayListPanel(fslpanel.FSLEyesPanel): tooltip = overlay.dataSource - self._listBox.Append(name, overlay, tooltip) + self.__listBox.Append(name, overlay, tooltip) widget = ListItemWidget(self, overlay, display, self._displayCtx, - self._listBox) + self.__listBox) - self._listBox.SetItemWidget(i, widget) + self.__listBox.SetItemWidget(i, widget) display.addListener('name', self._name, - self._overlayNameChanged, + self.__overlayNameChanged, overwrite=True) if len(self._overlayList) > 0: - self._listBox.SetSelection( + self.__listBox.SetSelection( self._displayCtx.getOverlayOrder( self._displayCtx.selectedOverlay)) - def _lbMove(self, ev): + def __lbMove(self, ev): """Called when an overlay is moved in the :class:`.EditableListBox`. - Reorders the :class:`.OverlayList` to reflect the change. + Reorders the :attr:`.DisplayContext.overlayOrder` to reflect the + change. """ self._displayCtx.disableListener('overlayOrder', self._name) self._displayCtx.overlayOrder.move(ev.oldIdx, ev.newIdx) self._displayCtx.enableListener('overlayOrder', self._name) - def _lbSelect(self, ev): + def __lbSelect(self, ev): """Called when an overlay is selected in the - :class:`.EditableListBox`. Sets the + :class:`.EditableListBox`. Updates the :attr:`.DisplayContext.selectedOverlay` property. """ self._displayCtx.disableListener('selectedOverlay', self._name) @@ -303,28 +203,236 @@ class OverlayListPanel(fslpanel.FSLEyesPanel): self._displayCtx.enableListener('selectedOverlay', self._name) - def _lbAdd(self, ev): - """Called when the 'add' button on the list box is pressed. - + def __lbAdd(self, ev): + """Called when the *add* button on the list box is pressed. Calls the :meth:`.OverlayList.addOverlays` method. """ if self._overlayList.addOverlays(): self._displayCtx.selectedOverlay = len(self._overlayList) - 1 - def _lbRemove(self, ev): + def __lbRemove(self, ev): """Called when an item is removed from the overlay listbox. - Removes the corresponding overlay from the :class:`.OverlayList`. """ self._overlayList.pop(self._displayCtx.overlayOrder[ev.idx]) - def _lbDblClick(self, ev): - """Called when an item label is double clickedon the overlay list - box. Toggles the enabled state of the overlay. + def __lbDblClick(self, ev): + """Called when an item label is double clicked on the overlay list + box. Toggles the visibility of the overlay, via the + :attr:`.Display.enabled` property.. """ idx = self._displayCtx.overlayOrder[ev.idx] overlay = self._overlayList[idx] display = self._displayCtx.getDisplay(overlay) display.enabled = not display.enabled + + +class ListItemWidget(wx.Panel): + """A ``LisItemWidget`` is created by the :class:`OverlayListPanel` for + every overlay in the :class:`.OverlayList`. A ``LisItemWidget`` contains + controls which allow the user to: + + - Toggle the visibility of the overlay (via the :attr:`.Display.enabled` + property) + + - Add the overlay to a group (see the + :attr:`.DisplayContext.overlayGroups` property, and the :mod:`.group` + module). + + - Save the overlay (if it has been modified). + + .. note:: While the :class:`.DisplayContext` allows multiple + :class:`.OverlayGroup` instances to be defined (and added to its + :attr:`.DisplayContext.overlayGroups` property), *FSLeyes* + currently only defines a single group . This ``OverlayGroup`` + is created in the :func:`.fsleyes.context` function, and overlays + can be added/removed to/from it via the *lock* button on a + ``ListItemWidget``. This functionality might change in a future + version of *FSLeyes*. + + + .. note:: Currently, only :class:`.Image` overlays can be saved. The *save* + button is disabled for all other overlay types. + """ + + + enabledFG = '#000000' + """This colour is used as the foreground (text) colour for overlays + where their :attr:`.Display.enabled` property is ``True``. + """ + + + disabledFG = '#CCCCCC' + """This colour is used as the foreground (text) colour for overlays + where their :attr:`.Display.enabled` property is ``False``. + """ + + + unsavedDefaultBG = '#ffaaaa' + """This colour is used as the default background colour for + :class:`.Image` overlays with an :attr:`.Image.saved` property + of ``False``. + """ + + + unsavedSelectedBG = '#aa4444' + """This colour is used as the background colour for :class:`.Image` + overlays with an :attr:`.Image.saved` property of ``False``, when + they are selected in the :class:`OverlayListPanel`. + """ + + + def __init__(self, parent, overlay, display, displayCtx, listBox): + """Create a ``ListItemWidget``. + + :arg parent: The :mod:`wx` parent object. + :arg overlay: The overlay associated with this ``ListItemWidget``. + :arg display: The :class:`.Display` associated with the overlay. + :arg displayCtx: The :class:`.DisplayContext` instance. + :arg listBox: The :class:`.EditableListBox` that contains this + ``ListItemWidget``. + """ + wx.Panel.__init__(self, parent) + + self.__overlay = overlay + self.__display = display + self.__displayCtx = displayCtx + self.__listBox = listBox + self.__name = '{}_{}'.format(self.__class__.__name__, id(self)) + + # BU_NOTEXT causes a segmentation fault under OSX + if wx.Platform == '__WXMAC__': btnStyle = wx.BU_EXACTFIT + else: btnStyle = wx.BU_EXACTFIT | wx.BU_NOTEXT + + self.__saveButton = wx.Button( self, style=btnStyle) + self.__lockButton = wx.ToggleButton(self, style=btnStyle) + + self.__saveButton.SetBitmap(icons.loadBitmap('floppydisk16')) + self.__lockButton.SetBitmap(icons.loadBitmap('chainlink16')) + + self.__visibility = props.makeWidget( + self, + display, + 'enabled', + icon=icons.findImageFile('eye16')) + + self.__sizer = wx.BoxSizer(wx.HORIZONTAL) + + self.SetSizer(self.__sizer) + + self.__sizer.Add(self.__saveButton, flag=wx.EXPAND, proportion=1) + self.__sizer.Add(self.__lockButton, flag=wx.EXPAND, proportion=1) + self.__sizer.Add(self.__visibility, flag=wx.EXPAND, proportion=1) + + # There is currently only one overlay + # group in the application. In the + # future there may be multiple groups. + group = displayCtx.overlayGroups[0] + + display.addListener('enabled', + self.__name, + self.__vizChanged) + group .addListener('overlays', + self.__name, + self.__overlayGroupChanged) + + if isinstance(overlay, fslimage.Image): + overlay.addListener('saved', self.__name, self.__saveStateChanged) + else: + self.__saveButton.Enable(False) + + self.__saveButton.Bind(wx.EVT_BUTTON, self.__onSaveButton) + self.__lockButton.Bind(wx.EVT_TOGGLEBUTTON, self.__onLockButton) + self .Bind(wx.EVT_WINDOW_DESTROY, self.__onDestroy) + + self.__overlayGroupChanged() + self.__vizChanged() + self.__saveStateChanged() + + + def __overlayGroupChanged(self, *a): + """Called when the :class:`.OverlayGroup` changes. Updates the *lock* + button based on whether the overlay associated with this + ``ListItemWidget`` is in the group or not. + """ + group = self.__displayCtx.overlayGroups[0] + self.__lockButton.SetValue(self.__overlay in group.overlays) + + + def __onSaveButton(self, ev): + """Called when the *save* button is pushed. Calls the + :meth:`.Image.save` method. + """ + self.__displayCtx.selectOverlay(self.__overlay) + self.__overlay.save() + + + def __onLockButton(self, ev): + """Called when the *lock* button is pushed. Adds/removes the overlay + to/from the :class:`.OverlayGroup`. + """ + self.__displayCtx.selectOverlay(self.__overlay) + group = self.__displayCtx.overlayGroups[0] + + if self.__lockButton.GetValue(): group.addOverlay( self.__overlay) + else: group.removeOverlay(self.__overlay) + + + def __onDestroy(self, ev): + """Called when this ``ListItemWidget`` is destroyed (i.e. when the + associated overlay is removed from the :class:`OverlayListPanel`). + Removes some proprety listeners from the :class:`.Display` and + :class:`.OverlayGroup` instances, and from the overlay if it is an + :class:`.Image` instance. + """ + ev.Skip() + if ev.GetEventObject() is not self: + return + + group = self.__displayCtx.overlayGroups[0] + + self.__display.removeListener('enabled', self.__name) + group .removeListener('overlays', self.__name) + + if isinstance(self.__overlay, fslimage.Image): + self.__overlay.removeListener('saved', self.__name) + + + def __saveStateChanged(self, *a): + """If the overlay is an :class:`.Image` instance, this method is + called when its :attr:`.Image.saved` property changes. Updates the + state of the *save* button. + """ + + if not isinstance(self.__overlay, fslimage.Image): + return + + idx = self.__listBox.IndexOf(self.__overlay) + + self.__saveButton.Enable(not self.__overlay.saved) + + if self.__overlay.saved: + self.__listBox.SetItemBackgroundColour(idx) + + else: + self.__listBox.SetItemBackgroundColour( + idx, + ListItemWidget.unsavedDefaultBG, + ListItemWidget.unsavedSelectedBG), + + + def __vizChanged(self, *a): + """Called when the :attr:`.Display.enabled` property of the overlay + changes. Updates the state of the *enabled* buton, and changes the + item foreground colour. + """ + self.__displayCtx.selectOverlay(self.__overlay) + + idx = self.__listBox.IndexOf(self.__overlay) + + if self.__display.enabled: fgColour = ListItemWidget.enabledFG + else: fgColour = ListItemWidget.disabledFG + + self.__listBox.SetItemForegroundColour(idx, fgColour) diff --git a/fsl/fsleyes/controls/shellpanel.py b/fsl/fsleyes/controls/shellpanel.py index 1ac8b1dcddd1124acf8aff4519e954957dd5d6b4..82ffd983e47da9c26f2700819adb6bad7e76ee37 100644 --- a/fsl/fsleyes/controls/shellpanel.py +++ b/fsl/fsleyes/controls/shellpanel.py @@ -1,9 +1,13 @@ #!/usr/bin/env python # -# shellpanel.py - +# shellpanel.py - The ShellPanel class. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # +"""This module provides the :class:`ShellPanel` class, a *FSLeyes control* +which contains an interactive Python shell. +""" + import wx @@ -13,8 +17,29 @@ import fsl.fsleyes.panel as fslpanel class ShellPanel(fslpanel.FSLEyesPanel): + """A ``ShellPanel`` is a :class:`.FSLEyesPanel` which contains an + interactive Python shell. + + A ``ShellPanel`` allows the user to programmatically interact with the + :class:`.OverlayList`, and with the :class:`.DisplayContext` and + :class:`.SceneOpts` instances associated with the :class:`.CanvasPanel` + that owns this ``ShellPanel``. + """ def __init__(self, parent, overlayList, displayCtx, sceneOpts): + """Create a ``ShellPanel``. + + :arg parent: The :mod:`wx` parent object, assumed to be the + :class:`.CanvasPanel` that owns this ``ShellPanel``. + + :arg overlayList: The :class:`.OverlayList`. + + :arg displayCtx: The :class:`.DisplayContext` of the + :class:`.CanvasPanel` that owns this ``ShellPanel``. + + :arg sceneOpts: The :class:`.SceneOpts` of the + :class:`.CanvasPanel` that owns this ``ShellPanel``. + """ fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) lcls = { @@ -30,7 +55,8 @@ class ShellPanel(fslpanel.FSLEyesPanel): 'Available variables are:\n' ' - overlayList\n' ' - displayCtx\n' - ' - sceneOpts\n\n', + ' - sceneOpts\n\n' + ' - viewPanel\n\n', locals=lcls, showInterpIntro=False) @@ -62,4 +88,7 @@ class ShellPanel(fslpanel.FSLEyesPanel): def destroy(self): + """Must be called when this ``ShellPanel`` is no longer needed. + Calls the :meth:`.FSLEyesPanel.destroy` method. + """ fslpanel.FSLEyesPanel.destroy(self) diff --git a/fsl/fsleyes/controls/timeseriescontrolpanel.py b/fsl/fsleyes/controls/timeseriescontrolpanel.py index 7289a67e6fff3655a9b7aa9ec6362374fb20c2cf..c13d08a6fbedd89753fa51f6639f9c5658ca8e91 100644 --- a/fsl/fsleyes/controls/timeseriescontrolpanel.py +++ b/fsl/fsleyes/controls/timeseriescontrolpanel.py @@ -1,9 +1,13 @@ #!/usr/bin/env python # -# timeseriescontrolpanel.py - +# timeseriescontrolpanel.py - The TimeSeriesControlPanel class. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # +"""This module provides the :class:`TimeSeriesControlPanel` a *FSLeyes +control* which allows the user to configure a :class:`.TimeSeriesPanel`. +""" + import wx @@ -16,8 +20,52 @@ import fsl.data.strings as strings class TimeSeriesControlPanel(fslpanel.FSLEyesPanel): + """The ``TimeSeriesControlPanel`` is a :class:`.FSLEyesPanel` which allows + the user to configure a :class:`.TimeSeriesPanel`. It contains controls + which are linked to the properties of the :class:`.TImeSeriesPanel`, + (which include properties defined on the :class:`.PlotPanel` base class), + and the :class:`.TimeSeries` class. + + + A ``TimeSeriesControlPanel`` looks something like this: + + .. image:: images/timeseriescontrolpanel.png + :scale: 50% + :align: center + + The settings shown on a ``TimeSeriesControlPanel`` are organised into three + or four sections: + + - The *Time series plot settings* section has controls which are linked to + properties of the :class:`.TimeSeriesPanel` class. + + - The *General plot settings* section has controls which are linked to + properties of the :class:`.PlotPanel` base class. + + - The *Settings for the current time course* section has controls which + are linked to properties of the :class:`.TimeSeries` class. These + properties define how the *current* time course is displayed (see the + :class:`.TimeSeriesPanel` class documentation). + + - The *FEAT plot settings* is only shown if the currently selected overlay + is a :class:`.FEATImage`. It has controls which are linked to properties + of the :class:`.FEATTimeSeries` class. + """ + + def __init__(self, parent, overlayList, displayCtx, tsPanel): + """Create a ``TimeSeriesControlPanel``. + + :arg parent: The :mod:`wx` parent object. + + :arg overlayList: The :class:`.OverlayList`. + + :arg displayCtx: The :class:`.DisplayContext` instance. + + :arg tsPanel: The :class:`.TimeSeriesPanel` associated with this + ``TimeSeriesControlPanel``. + """ fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx) @@ -128,6 +176,10 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel): def destroy(self): + """Must be called when this ``TimeSeriesControlPanel`` is no longer + needed. Removes some property listeners, and calls the + :meth:`.FSLEyesPanel.destroy` method. + """ self._displayCtx .removeListener('selectedOverlay', self._name) self._overlayList.removeListener('overlays', self._name) @@ -139,6 +191,10 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel): def __showCurrentChanged(self, *a): + """Called when the :attr:`.TimeSeriesPanel.showCurrent` property + changes. Shows hides the *Settings for the current time course* + section. + """ widgets = self.__widgets tsPanel = self.__tsPanel showCurrent = tsPanel.showCurrent @@ -185,6 +241,11 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel): def __selectedOverlayNameChanged(self, *a): + """Called when the :attr:`.Display.name` property for the currently + selected overlay changes. Only called if the current overlay is a + :class:`.FEATImage`. Updates the display name of the *FEAT plot + settings* section. + """ display = self._displayCtx.getDisplay(self.__selectedOverlay) self.__widgets.RenameGroup( 'currentFEATSettings', @@ -193,6 +254,10 @@ class TimeSeriesControlPanel(fslpanel.FSLEyesPanel): def __selectedOverlayChanged(self, *a): + """Called when the :attr:`.DisplayContext.selectedOverlay` or + :class:`.OverlayList` changes. If the newly selected overlay is a + :class:`.FEATImage`, the *FEAT plot settings* section is updated. + """ # We're assuminbg that the TimeSeriesPanel has # already updated its current TimeSeries for diff --git a/fsl/fsleyes/overlay.py b/fsl/fsleyes/overlay.py index 39300f0ee51202a3901e176f832e1bb07964b5b8..c6d6fbf24709fb6fd77e1f00d0ccc772c127ff8c 100644 --- a/fsl/fsleyes/overlay.py +++ b/fsl/fsleyes/overlay.py @@ -40,6 +40,7 @@ Currently (``fslpy`` version |version|) the only overlay types in existence :nosignatures: ~fsl.data.image.Image + ~fsl.data.featimage.FEATImage ~fsl.data.model.Model @@ -53,7 +54,6 @@ A few other utility functions are provided by this module: loadOverlays interactiveLoadOverlays saveOverlay - """ import logging