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

Making the time series panel fancy. Time courses can be persisted;

display properties can be modified via little widgets on time series
list panel.
parent c1db01a5
No related branches found
No related tags found
No related merge requests found
......@@ -5,13 +5,49 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import wx
import wx
import numpy as np
import props
import pwidgets.elistbox as elistbox
import fsl.fslview.panel as fslpanel
import fsl.utils.transform as transform
import fsl.fslview.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')
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.FSLViewPanel):
def __init__(self, parent, overlayList, displayCtx, timeSeriesPanel):
......@@ -19,8 +55,10 @@ class TimeSeriesListPanel(fslpanel.FSLViewPanel):
fslpanel.FSLViewPanel.__init__(self, parent, overlayList, displayCtx)
self.__tsPanel = timeSeriesPanel
self.__currentLabel = wx.StaticText( self)
self.__tsList = elistbox.EditableListBox(self)
self.__currentLabel = wx.StaticText(self)
self.__tsList = elistbox.EditableListBox(
self, style=(elistbox.ELB_NO_MOVE |
elistbox.ELB_EDITABLE))
self.__sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.__sizer)
......@@ -28,14 +66,23 @@ class TimeSeriesListPanel(fslpanel.FSLViewPanel):
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.__tsList.Bind(elistbox.EVT_ELB_ADD_EVENT, self.__onListAdd)
self.__tsList.Bind(elistbox.EVT_ELB_REMOVE_EVENT, self.__onListRemove)
self.__tsList.Bind(elistbox.EVT_ELB_EDIT_EVENT, self.__onListEdit)
self.__tsList.Bind(elistbox.EVT_ELB_SELECT_EVENT, self.__onListSelect)
displayCtx .addListener('selectedOverlay',
self._name,
self.__locationChanged)
overlayList .addListener('overlays',
self._name,
self.__locationChanged)
self.__tsPanel.addListener('timeSeries',
self._name,
self.__timeSeriesChanged)
self.__timeSeriesChanged()
self.__locationChanged()
self.Layout()
......@@ -43,6 +90,16 @@ class TimeSeriesListPanel(fslpanel.FSLViewPanel):
fslpanel.FSLViewPanel.destroy(self)
self._displayCtx .removeListener('selectedOverlay', self._name)
self._overlayList.removeListener('overlays', self._name)
self.__tsPanel .removeListener('timeSeries', self._name)
def __timeSeriesChanged(self, *a):
self.__tsList.Clear()
for ts in self.__tsPanel.timeSeries:
widg = TimeSeriesWidget(self, ts)
self.__tsList.Append(ts.label, clientData=ts, extraWidget=widg)
def __locationChanged(self, *a):
......@@ -53,7 +110,33 @@ class TimeSeriesListPanel(fslpanel.FSLViewPanel):
ts = self.__tsPanel.getCurrent()
ts.colour = fslcm.randomColour()
if ts is None:
return
ts.alpha = 1
ts.lineWidth = 1
ts.lineStyle = '-'
ts.colour = fslcm.randomColour()
ts.label = '{} [{} {} {}]'.format(ts.overlay.name,
ts.coords[0],
ts.coords[1],
ts.coords[2])
self.__tsPanel.timeSeries.append(ts)
def __onListEdit(self, ev):
ev.data.label = ev.label
def __onListSelect(self, ev):
opts = self._displayCtx.getOpts(ev.data.overlay)
vox = np.array(ev.data.coords)
xform = opts.getTransform('voxel', 'display')
disp = transform.transform([vox], xform)[0]
self._displayCtx.location = disp
def __onListRemove(self, ev):
self.__tsPanel.timeSeries.remove(ev.data)
......@@ -40,14 +40,22 @@ log = logging.getLogger(__name__)
# of each persistent time course
class TimeSeries(object):
def __init__(self, overlay, coords, data, colour, label):
class TimeSeries(props.HasProperties):
colour = props.Colour()
alpha = props.Real(minval=0.0, maxval=1.0, default=1.0, clamped=True)
label = props.String()
lineWidth = props.Choice((1, 2, 3, 4, 5))
lineStyle = props.Choice(
*zip(*[('-', 'Solid line'),
('--', 'Dashed line'),
('-.', 'Dash-dot line'),
(':', 'Dotted line')]))
def __init__(self, overlay, coords, data):
self.overlay = overlay
self.coords = coords
self.coords = map(int, coords)
self.data = data
self.colour = colour
self.label = label
class TimeSeriesPanel(plotpanel.PlotPanel):
......@@ -60,6 +68,16 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
timeSeries = props.List()
demean = props.Boolean(default=True)
legend = props.Boolean(default=True)
def export(self, *a):
# Export all displayed time series to text file
pass
def __init__(self, parent, overlayList, displayCtx):
......@@ -82,10 +100,15 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
name = self._name
draw = self._draw
def tsChanged(*a):
for ts in self.timeSeries:
ts.addGlobalListener(name, draw, overwrite=True)
draw()
self._overlayList.addListener('overlays', name, draw)
self._displayCtx .addListener('selectedOverlay', name, draw)
self._displayCtx .addListener('location', name, draw)
self .addListener('timeSeries', name, draw)
self .addListener('timeSeries', name, tsChanged)
self.Layout()
self._draw()
......@@ -101,6 +124,9 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
def getCurrent(self):
if len(self._overlayList) == 0:
return None
x, y, z = self._displayCtx.location.xyz
overlay = self._displayCtx.getSelectedOverlay()
opts = self._displayCtx.getOpts(overlay)
......@@ -122,12 +148,15 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
vox[2] >= overlay.shape[2]:
return None
return TimeSeries(
ts = TimeSeries(
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]))
overlay.data[vox[0], vox[1], vox[2], :])
ts.colour = [0.2, 0.2, 0.2]
ts.lineWidth = 1
ts.lineStyle = ':'
ts.label = None
return ts
def _draw(self, *a):
......@@ -141,7 +170,7 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
currentTs = self.getCurrent()
if currentTs is not None:
toPlot = [currentTs] + toPlot
toPlot = [currentTs] + toPlot
if len(toPlot) == 0:
canvas.draw()
......@@ -152,7 +181,10 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
ylims = []
for ts in toPlot:
xlim, ylim = self._drawTimeSeries(ts)
if ts is currentTs:
xlim, ylim = self._drawTimeSeries(ts)
else:
xlim, ylim = self._drawTimeSeries(ts)
xlims.append(xlim)
ylims.append(ylim)
......@@ -162,8 +194,22 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
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))
xpad = 0.05 * (xmax - xmin)
ypad = 0.05 * (ymax - ymin)
axis.set_xlim((xmin - xpad, xmax + xpad))
axis.set_ylim((ymin - ypad, ymax + ypad))
# legend - don't show if we're only
# plotting the current location
if len(self.timeSeries) > 0 and self.legend:
handles, labels = axis.get_legend_handles_labels()
legend = axis.legend(
handles,
labels,
loc='upper right',
fancybox=True)
legend.get_frame().set_alpha(0.3)
canvas.draw()
self.Refresh()
......@@ -171,14 +217,22 @@ class TimeSeriesPanel(plotpanel.PlotPanel):
def _drawTimeSeries(self, ts):
display = self._displayCtx.getDisplay(ts.overlay)
if ts.alpha == 0:
return (0, 0), (0, 0)
data = ts.data
if not display.enabled:
return None
if self.demean:
data = data - data.mean()
data = ts.data
kwargs = {}
kwargs['lw'] = ts.lineWidth
kwargs['alpha'] = ts.alpha
kwargs['color'] = ts.colour
kwargs['label'] = ts.label
kwargs['ls'] = ts.lineStyle
self.getAxis().plot(data, lw=2, c=ts.colour, label=ts.label)
self.getAxis().plot(data, **kwargs)
# TODO take into account TR
return (0, len(data)), (data.min(), data.max())
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