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

In process of improving time series functionality.

parent 8133eaf1
No related branches found
No related tags found
No related merge requests found
...@@ -116,6 +116,7 @@ titles = TypeDict({ ...@@ -116,6 +116,7 @@ titles = TypeDict({
'LookupTablePanel' : 'Lookup tables', 'LookupTablePanel' : 'Lookup tables',
'LutLabelDialog' : 'New LUT label', 'LutLabelDialog' : 'New LUT label',
'NewLutDialog' : 'New LUT', 'NewLutDialog' : 'New LUT',
'TimeSeriesListPanel' : 'Time series list',
'LookupTablePanel.loadLut' : 'Select a lookup table file', 'LookupTablePanel.loadLut' : 'Select a lookup table file',
'LookupTablePanel.labelExists' : 'Label already exists', 'LookupTablePanel.labelExists' : 'Label already exists',
...@@ -148,9 +149,11 @@ actions = TypeDict({ ...@@ -148,9 +149,11 @@ actions = TypeDict({
'LightBoxPanel.toggleLightBoxToolBar' : 'View properties', 'LightBoxPanel.toggleLightBoxToolBar' : 'View properties',
'PlotPanel.screenshot' : 'Take screenshot', 'PlotPanel.screenshot' : 'Take screenshot',
'HistogramPanel.toggleToolbar' : 'Histogram controls', 'TimeSeriesPanel.toggleTimeSeriesList' : 'Time series list',
'HistogramPanel.toggleToolbar' : 'Histogram controls',
'OrthoViewProfile.centreCursor' : 'Centre cursor', 'OrthoViewProfile.centreCursor' : 'Centre cursor',
......
...@@ -13,6 +13,7 @@ from lightboxsettingspanel import LightBoxSettingsPanel ...@@ -13,6 +13,7 @@ from lightboxsettingspanel import LightBoxSettingsPanel
from locationpanel import LocationPanel from locationpanel import LocationPanel
from orthosettingspanel import OrthoSettingsPanel from orthosettingspanel import OrthoSettingsPanel
from lookuptablepanel import LookupTablePanel from lookuptablepanel import LookupTablePanel
from timeserieslistpanel import TimeSeriesListPanel
from orthotoolbar import OrthoToolBar from orthotoolbar import OrthoToolBar
from orthoprofiletoolbar import OrthoProfileToolBar from orthoprofiletoolbar import OrthoProfileToolBar
......
#!/usr/bin/env python
#
# timeserieslistpanel.py -
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import wx
import pwidgets.elistbox as elistbox
import fsl.fslview.panel as fslpanel
import fsl.fslview.colourmaps as fslcm
class TimeSeriesListPanel(fslpanel.FSLViewPanel):
def __init__(self, parent, overlayList, displayCtx, timeSeriesPanel):
fslpanel.FSLViewPanel.__init__(self, parent, overlayList, displayCtx)
self.__tsPanel = timeSeriesPanel
self.__currentLabel = wx.StaticText( self)
self.__tsList = elistbox.EditableListBox(self)
self.__sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.__sizer)
self.__sizer.Add(self.__currentLabel, flag=wx.EXPAND)
self.__sizer.Add(self.__tsList, flag=wx.EXPAND, proportion=1)
displayCtx .addListener('selectedOverlay',
self._name,
self.__locationChanged)
overlayList.addListener('overlays',
self._name,
self.__locationChanged)
self.Layout()
def destroy(self):
fslpanel.FSLViewPanel.destroy(self)
self._displayCtx .removeListener('selectedOverlay', self._name)
self._overlayList.removeListener('overlays', self._name)
def __locationChanged(self, *a):
pass
def __onListAdd(self, ev):
ts = self.__tsPanel.getCurrent()
ts.colour = fslcm.randomColour()
self.__tsPanel.timeSeries.append(ts)
...@@ -16,10 +16,14 @@ import logging ...@@ -16,10 +16,14 @@ import logging
import numpy as np import numpy as np
import props
import plotpanel import plotpanel
import fsl.data.image as fslimage import fsl.data.image as fslimage
import fsl.data.strings as strings import fsl.data.strings as strings
import fsl.fslview.displaycontext as fsldisplay import fsl.fslview.displaycontext as fsldisplay
import fsl.fslview.colourmaps as fslcm
import fsl.fslview.controls as fslcontrols
import fsl.utils.transform as transform import fsl.utils.transform as transform
...@@ -36,6 +40,16 @@ log = logging.getLogger(__name__) ...@@ -36,6 +40,16 @@ log = logging.getLogger(__name__)
# of each persistent time course # of each persistent time course
class TimeSeries(object):
def __init__(self, overlay, coords, data, colour, label):
self.overlay = overlay
self.coords = coords
self.data = data
self.colour = colour
self.label = label
class TimeSeriesPanel(plotpanel.PlotPanel): class TimeSeriesPanel(plotpanel.PlotPanel):
"""A panel with a :mod:`matplotlib` canvas embedded within. """A panel with a :mod:`matplotlib` canvas embedded within.
...@@ -44,38 +58,37 @@ class TimeSeriesPanel(plotpanel.PlotPanel): ...@@ -44,38 +58,37 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
plotted on the canvas. plotted on the canvas.
""" """
timeSeries = props.List()
def __init__(self, parent, overlayList, displayCtx): def __init__(self, parent, overlayList, displayCtx):
plotpanel.PlotPanel.__init__(self, parent, overlayList, displayCtx) actionz = {
'toggleTimeSeriesList' : lambda *a: self.togglePanel(
fslcontrols.TimeSeriesListPanel, False, self)
}
plotpanel.PlotPanel.__init__(
self, parent, overlayList, displayCtx, actionz=actionz)
figure = self.getFigure() figure = self.getFigure()
canvas = self.getCanvas()
figure.subplots_adjust( figure.subplots_adjust(
top=1.0, bottom=0.0, left=0.0, right=1.0) top=1.0, bottom=0.0, left=0.0, right=1.0)
figure.patch.set_visible(False) figure.patch.set_visible(False)
self._mouseDown = False name = self._name
canvas.mpl_connect('button_press_event', self._onPlotMouseDown) draw = self._draw
canvas.mpl_connect('button_release_event', self._onPlotMouseUp)
canvas.mpl_connect('motion_notify_event', self._onPlotMouseMove) self._overlayList.addListener('overlays', name, draw)
self._displayCtx .addListener('selectedOverlay', name, draw)
self._overlayList.addListener( self._displayCtx .addListener('location', name, draw)
'overlays', self .addListener('timeSeries', name, draw)
self._name,
self._selectedOverlayChanged)
self._displayCtx.addListener(
'selectedOverlay',
self._name,
self._selectedOverlayChanged)
self._displayCtx.addListener(
'location',
self._name,
self._locationChanged)
self.Layout() self.Layout()
self._selectedOverlayChanged() self._draw()
def destroy(self): def destroy(self):
...@@ -85,126 +98,87 @@ class TimeSeriesPanel(plotpanel.PlotPanel): ...@@ -85,126 +98,87 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
self._displayCtx .removeListener('selectedOverlay', self._name) self._displayCtx .removeListener('selectedOverlay', self._name)
self._displayCtx .removeListener('location', self._name) self._displayCtx .removeListener('location', self._name)
for ovl in self._overlayList:
opts = self._displayCtx.getOpts(ovl)
if isinstance(opts, fsldisplay.ImageOpts):
opts.removeListener('volume', self._name)
def _selectedOverlayChanged(self, *a):
overlay = self._displayCtx.getSelectedOverlay()
for ovl in self._overlayList:
if not isinstance(ovl, fslimage.Image):
continue
opts = self._displayCtx.getOpts(ovl)
if ovl is overlay:
opts.addListener('volume',
self._name,
self._locationChanged,
overwrite=True)
else:
opts.removeListener('volume', self._name)
self._locationChanged()
def _locationChanged(self, *a): def getCurrent(self):
self.getAxis().clear()
if len(self._overlayList) == 0:
self.getCanvas().draw()
self.Refresh()
else:
self._drawPlot()
def _drawPlot(self):
axis = self.getAxis()
canvas = self.getCanvas()
x, y, z = self._displayCtx.location.xyz x, y, z = self._displayCtx.location.xyz
overlay = self._displayCtx.getSelectedOverlay() overlay = self._displayCtx.getSelectedOverlay()
opts = self._displayCtx.getOpts(overlay)
if not isinstance(overlay, fslimage.Image): if not isinstance(overlay, fslimage.Image) or \
self.message(strings.messages[self, 'noData']) not isinstance(opts, fsldisplay.VolumeOpts) or \
not overlay.is4DImage():
elif not overlay.is4DImage(): return None
self.message(strings.messages[self, 'not4D'])
xform = opts.getTransform('display', 'voxel')
vox = transform.transform([[x, y, z]], xform)[0]
vox = np.floor(vox + 0.5)
else: if vox[0] < 0 or \
opts = self._displayCtx.getOpts(overlay) vox[1] < 0 or \
xform = opts.getTransform('display', 'voxel') vox[2] < 0 or \
vox[0] >= overlay.shape[0] or \
vox[1] >= overlay.shape[1] or \
vox[2] >= overlay.shape[2]:
return None
vox = transform.transform([[x, y, z]], xform)[0] return TimeSeries(
vox = np.floor(vox + 0.5) overlay,
vox,
overlay.data[vox[0], vox[1], vox[2], :],
[0.5, 0.5, 0.5],
'{} [{} {} {}]'.format(overlay.name, vox[0], vox[1], vox[2]))
if vox[0] < 0 or \
vox[1] < 0 or \
vox[2] < 0 or \
vox[0] >= overlay.shape[0] or \
vox[1] >= overlay.shape[1] or \
vox[2] >= overlay.shape[2]:
self.message(strings.messages[self, 'outOfBounds'])
else: def _draw(self, *a):
self._drawPlotOneOverlay(overlay, *vox)
axis.axvline(opts.volume, c='#000080', lw=2, alpha=0.4)
canvas.draw() axis = self.getAxis()
self.Refresh() canvas = self.getCanvas()
axis.clear()
def _drawPlotOneOverlay(self, overlay, x, y, z): toPlot = self.timeSeries[:]
currentTs = self.getCurrent()
display = self._displayCtx.getDisplay(overlay) if currentTs is not None:
toPlot = [currentTs] + toPlot
if not overlay.is4DImage(): return None if len(toPlot) == 0:
if not display.enabled: return None canvas.draw()
self.Refresh()
return
for vox, shape in zip((x, y, z), overlay.shape): xlims = []
if vox >= shape or vox < 0: ylims = []
return None
data = overlay.data[x, y, z, :] for ts in toPlot:
self.getAxis().plot(data, lw=2) xlim, ylim = self._drawTimeSeries(ts)
xlims.append(xlim)
ylims.append(ylim)
return data.min(), data.max(), len(data) # Set x/ylim
xmin = min([lim[0] for lim in xlims])
xmax = max([lim[1] for lim in xlims])
ymin = min([lim[0] for lim in ylims])
ymax = max([lim[1] for lim in ylims])
axis.set_xlim((xmin, xmax))
axis.set_ylim((ymin, ymax))
def _onPlotMouseDown(self, ev): canvas.draw()
if ev.inaxes != self.getAxis(): return self.Refresh()
overlay = self._displayCtx.getSelectedOverlay()
if not isinstance(overlay, fslimage.Image) or not overlay.is4DImage(): def _drawTimeSeries(self, ts):
return
self._mouseDown = True
opts = self._displayCtx.getOpts(overlay) display = self._displayCtx.getDisplay(ts.overlay)
opts.volume = np.floor(ev.xdata)
def _onPlotMouseUp(self, ev): if not display.enabled:
self._mouseDown = False return None
data = ts.data
def _onPlotMouseMove(self, ev): self.getAxis().plot(data, lw=2, c=ts.colour, label=ts.label)
if not self._mouseDown: return
if ev.inaxes != self.getAxis(): return
overlay = self._displayCtx.getSelectedOverlay()
if not isinstance(overlay, fslimage.Image) or not overlay.is4DImage(): # TODO take into account TR
return return (0, len(data)), (data.min(), data.max())
opts = self._displayCtx.getOpts(overlay)
opts.volume = np.floor(ev.xdata)
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