From 2ff28f6cbbf5fc2b15950528a8ba9c72940abb5c Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Thu, 21 May 2015 15:23:06 +0100 Subject: [PATCH] Quick rework of TimeSeriesPanel to handle non-volumetric overlays. Will be refactoring TimeSeriesPanel soon ... --- fsl/data/strings.py | 5 + fsl/fslview/views/histogrampanel.py | 14 +-- fsl/fslview/views/lightboxpanel.py | 3 - fsl/fslview/views/plotpanel.py | 12 +++ fsl/fslview/views/timeseriespanel.py | 156 +++++++++++++++------------ 5 files changed, 107 insertions(+), 83 deletions(-) diff --git a/fsl/data/strings.py b/fsl/data/strings.py index 51b826498..25bfbf20c 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -56,6 +56,11 @@ messages = TypeDict({ 'this command: \n{}', 'HistogramPanel.noData' : 'Selected overlay has no data', + 'TimeSeriesPanel.noData' : 'Selected overlay has no data', + 'TimeSeriesPanel.not4D' : 'Selected overlay is ' + 'not four dimensional', + 'TimeSeriesPanel.outOfBounds' : 'Selected overlay has no data ' + 'at the current coordinates', }) diff --git a/fsl/fslview/views/histogrampanel.py b/fsl/fslview/views/histogrampanel.py index 6970306f4..794ecc06b 100644 --- a/fsl/fslview/views/histogrampanel.py +++ b/fsl/fslview/views/histogrampanel.py @@ -160,7 +160,7 @@ class HistogramPanel(plotpanel.PlotPanel): overlay = self._displayCtx.getSelectedOverlay() if not isinstance(overlay, fslimage.Image): - self._displayMessage(strings.messages[self, 'noData']) + self.message(strings.messages[self, 'noData']) return minval = float(overlay.data.min()) @@ -212,18 +212,6 @@ class HistogramPanel(plotpanel.PlotPanel): self.dataRange.x = newRange - def _displayMessage(self, msg): - - axis = self.getAxis() - axis.clear() - axis.set_xlim((0.0, 1.0)) - axis.set_ylim((0.0, 1.0)) - axis.text(0.5, 0.5, msg, ha='center', va='center') - - self.getCanvas().draw() - self.Refresh() - - def _drawPlot(self, *a): overlay = self._displayCtx.getSelectedOverlay() diff --git a/fsl/fslview/views/lightboxpanel.py b/fsl/fslview/views/lightboxpanel.py index cf0ed926d..46e10c741 100644 --- a/fsl/fslview/views/lightboxpanel.py +++ b/fsl/fslview/views/lightboxpanel.py @@ -93,9 +93,6 @@ class LightBoxPanel(canvaspanel.CanvasPanel): self._displayCtx .addListener('selectedOverlay', self._name, self._selectedOverlayChanged) - self._displayCtx .addListener('overlayOrder', - self._name, - self._selectedOverlayChanged) self._overlayList.addListener('overlays', self._name, self._selectedOverlayChanged) diff --git a/fsl/fslview/views/plotpanel.py b/fsl/fslview/views/plotpanel.py index c13c23eb4..bc9343d7d 100644 --- a/fsl/fslview/views/plotpanel.py +++ b/fsl/fslview/views/plotpanel.py @@ -68,3 +68,15 @@ class PlotPanel(viewpanel.ViewPanel): def screenshot(self, *a): pass + + + def message(self, msg): + + axis = self.getAxis() + axis.clear() + axis.set_xlim((0.0, 1.0)) + axis.set_ylim((0.0, 1.0)) + axis.text(0.5, 0.5, msg, ha='center', va='center') + + self.getCanvas().draw() + self.Refresh() diff --git a/fsl/fslview/views/timeseriespanel.py b/fsl/fslview/views/timeseriespanel.py index c1dd27514..bb61bfbca 100644 --- a/fsl/fslview/views/timeseriespanel.py +++ b/fsl/fslview/views/timeseriespanel.py @@ -13,13 +13,19 @@ of overlay objects stored in an :class:`.OverlayList`. """ import logging + +import numpy as np + +import plotpanel +import fsl.data.image as fslimage +import fsl.data.strings as strings +import fsl.fslview.displaycontext as fsldisplay +import fsl.utils.transform as transform + + log = logging.getLogger(__name__) -import numpy as np -import plotpanel -import fsl.data.image as fslimage -import fsl.utils.transform as transform # TODO # - Whack a scrollbar in there, to allow @@ -58,98 +64,99 @@ class TimeSeriesPanel(plotpanel.PlotPanel): self._overlayList.addListener( 'overlays', self._name, - self._locationChanged) + self._selectedOverlayChanged) self._displayCtx.addListener( - 'location', + 'selectedOverlay', self._name, - self._locationChanged) + self._selectedOverlayChanged) self._displayCtx.addListener( - 'volume', + 'location', self._name, - self._locationChanged) + self._locationChanged) self.Layout() + self._selectedOverlayChanged() def destroy(self): plotpanel.PlotPanel.destroy(self) - self._overlayList.removeListener('overlays', self._name) - self._displayCtx .removeListener('location', self._name) - self._displayCtx .removeListener('volume', self._name) - - - def _locationChanged(self, *a): - - self.getAxis().clear() - - if len(self._overlayList) == 0: - return + self._overlayList.removeListener('overlays', self._name) + self._displayCtx .removeListener('selectedOverlay', self._name) + self._displayCtx .removeListener('location', self._name) - self._drawPlot() + for ovl in self._overlayList: + opts = self._displayCtx.getOpts(ovl) + if isinstance(opts, fsldisplay.ImageOpts): + opts.removeListener('volume', self._name) - def _drawPlot(self): - axis = self.getAxis() - canvas = self.getCanvas() - x, y, z = self._displayCtx.location.xyz - vol = self._displayCtx.volume + def _selectedOverlayChanged(self, *a): - mins = [] - maxs = [] - vols = [] + overlay = self._displayCtx.getSelectedOverlay() - for overlay in self._overlayList: + for ovl in self._overlayList: - if not isinstance(overlay, fslimage.Image): - log.warn('{}: Non-volumetric overlay types ' - 'not supported yet'.format(overlay)) + if not isinstance(ovl, fslimage.Image): continue - display = self._displayCtx.getDisplay(overlay) - xform = display.getTransform('display', 'voxel') - - ix, iy, iz = transform.transform([[x, y, z]], xform)[0] + opts = self._displayCtx.getOpts(ovl) - ix = round(ix) - iy = round(iy) - iz = round(iz) + if ovl is overlay: + opts.addListener('volume', + self._name, + self._locationChanged, + overwrite=True) + else: + opts.removeListener('volume', self._name) - minmaxvol = self._drawPlotOneOverlay(overlay, ix, iy, iz) - - if minmaxvol is not None: - mins.append(minmaxvol[0]) - maxs.append(minmaxvol[1]) - vols.append(minmaxvol[2]) + self._locationChanged() + + + + def _locationChanged(self, *a): + + self.getAxis().clear() + if len(self._overlayList) == 0: + self.getCanvas().draw() + self.Refresh() + else: + self._drawPlot() - axis.axvline(vol, c='#000080', lw=2, alpha=0.4) - if len(mins) > 0: + def _drawPlot(self): - xmin = 0 - xmax = max(vols) - 1 - ymin = min(mins) - ymax = max(maxs) + axis = self.getAxis() + canvas = self.getCanvas() + x, y, z = self._displayCtx.location.xyz + overlay = self._displayCtx.getSelectedOverlay() - xlen = xmax - xmin - ylen = ymax - ymin + if not isinstance(overlay, fslimage.Image): + self.message(strings.messages[self, 'noData']) - axis.grid(True) - axis.set_xlim((xmin - xlen * 0.05, xmax + xlen * 0.05)) - axis.set_ylim((ymin - ylen * 0.05, ymax + ylen * 0.05)) + elif not overlay.is4DImage(): + self.message(strings.messages[self, 'not4D']) - if ymin != ymax: yticks = np.linspace(ymin, ymax, 5) - else: yticks = [ymin] + else: + opts = self._displayCtx.getOpts(overlay) + xform = opts.getTransform('display', 'voxel') - axis.set_yticks(yticks) + vox = transform.transform([[x, y, z]], xform)[0] + vox = np.floor(vox + 0.5) - for tick in axis.yaxis.get_major_ticks(): - tick.set_pad(-15) - tick.label1.set_horizontalalignment('left') + 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]: - for tick in axis.xaxis.get_major_ticks(): - tick.set_pad(-20) + self.message(strings.messages[self, 'outOfBounds']) + + else: + self._drawPlotOneOverlay(overlay, *vox) + axis.axvline(opts.volume, c='#000080', lw=2, alpha=0.4) canvas.draw() self.Refresh() @@ -174,8 +181,15 @@ class TimeSeriesPanel(plotpanel.PlotPanel): def _onPlotMouseDown(self, ev): if ev.inaxes != self.getAxis(): return + + overlay = self._displayCtx.getSelectedOverlay() + + if not isinstance(overlay, fslimage.Image) or not overlay.is4DImage(): + return self._mouseDown = True - self._displayCtx.volume = np.floor(ev.xdata) + + opts = self._displayCtx.getOpts(overlay) + opts.volume = np.floor(ev.xdata) def _onPlotMouseUp(self, ev): @@ -185,4 +199,12 @@ class TimeSeriesPanel(plotpanel.PlotPanel): def _onPlotMouseMove(self, ev): if not self._mouseDown: return if ev.inaxes != self.getAxis(): return - self._displayCtx.volume = np.floor(ev.xdata) + + overlay = self._displayCtx.getSelectedOverlay() + + if not isinstance(overlay, fslimage.Image) or not overlay.is4DImage(): + return + + opts = self._displayCtx.getOpts(overlay) + + opts.volume = np.floor(ev.xdata) -- GitLab