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