Repository: mne-tools/mne-python

Issue:
interactivity in epochs.plot_topo_image requires plt.ion()
It appears that the "click to magnify this channel" interactivity only works if the user has already imported pyplot and called `plt.ion()`. Try this in a fresh ipython session:

```
import os
import mne
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False)
events = mne.find_events(raw, stim_channel='STI 014')
epochs = mne.Epochs(raw, events)
epochs.plot_topo_image()
# now click on any channel's imagemap, nothing happens
# now close the plot window, and...
import matplotlib.pyplot as plt
plt.show()  # only now does the enlarged image appear
```

Of course, if the last two steps aren't performed, then those orphaned figures hang around and only show up when the *next* MNE-Python plotting call is executed.  Would it work to use `matplotlib.is_interactive()` to get the ion/ioff state before the plot call, and if necessary turn it on for the duration of the plot, and then turn it back off again?




Discussion:
> Would it work to use matplotlib.is_interactive() to get the ion/ioff state before the plot call, and if necessary turn it on for the duration of the plot, and then turn it back off again?

It's probably not safe to mess with the `ion` / `ioff` state, it means that users can unexpectedly have code execute that they did not want to.

There is probably a way to get the newly spawned figures to appear. It's possible we need to call `plt.show()` after creating any new figures. It seems like something like that should work.

In a worst case, if we can't find a way to get the new ones to show up without messing with interactive state, we can emit a warning when the user clicks that the figure cannot be shown due to `ioff()`.
right, of course. calling `plt.show()` is a cleaner solution.
Turns out this problem is worse with `raw.plot_psd_topo()`:

```
(mnedev) [~] $ ipython
In [1]: import os 
   ...: import mne 
   ...: sample_data_folder = mne.datasets.sample.data_path() 
   ...: sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample', 
   ...:                                     'sample_audvis_raw.fif') 
   ...: raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False) 
   ...: raw.plot_psd_topo()                                                                                                           
Effective window size : 3.410 (s)
Out[1]: <Figure size 640x480 with 1 Axes>
can only concatenate list (not "NoneType") to list
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/mne-python/mne/viz/topo.py", line 259, in _plot_topo_onpick
    show_func(ax, ch_idx)
  File "/opt/mne-python/mne/viz/topo.py", line 420, in _plot_timeseries
    _setup_ax_spines(ax, vline, tmin, tmax)
  File "/opt/mne-python/mne/viz/utils.py", line 2335, in _setup_ax_spines
    xticks = sorted(list(set([x for x in axes.get_xticks()] + vlines)))
TypeError: can only concatenate list (not "NoneType") to list
In [2]: # above traceback happens when clicking on a channel. Closed window at this point, then:                                                                                          
In [3]: import matplotlib.pyplot as plt                                                                                               
In [4]: plt.show()                                                                                                                    
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/cbook/__init__.py", line 215, in process
    func(*args, **kwargs)
  File "/opt/miniconda3/envs/mnedev/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 2679, in mouse_move
    s = event.inaxes.format_coord(event.xdata, event.ydata)
  File "/opt/mne-python/mne/viz/topo.py", line 389, in <lambda>
    ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
  File "/opt/mne-python/mne/viz/topo.py", line 369, in _format_coord
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
  File "/opt/mne-python/mne/viz/topo.py", line 369, in <listcomp>
    nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
TypeError: 'NoneType' object is not subscriptable
In [5]: # this second traceback vomit was simply from mousing over the plot area,
In [6]: # and appears to be a separate issue; it happens even if plt.ion() is called 
In [7]: # before the call to raw.plot_psd_topo()
```

Looks like there are indeed a few bugs, can you see if the fix isn't too bad? Or let me know if you'd rather have me look
You can assign it to me, I'll look on Monday.

On March 29, 2019 8:05:20 PM AKDT, Eric Larson <notifications@github.com> wrote:
>Looks like there are indeed a few bugs, can you see if the fix isn't
>too bad? Or let me know if you'd rather have me look


Resolving patch:
diff --git a/mne/viz/topo.py b/mne/viz/topo.py
--- a/mne/viz/topo.py
+++ b/mne/viz/topo.py
@@ -256,6 +256,7 @@ def _plot_topo_onpick(event, show_func):
 
         # allow custom function to override parameters
         show_func(ax, ch_idx)
+        plt_show(fig=fig)
 
     except Exception as err:
         # matplotlib silently ignores exceptions in event handlers,
@@ -347,44 +348,38 @@ def _plot_timeseries(ax, ch_idx, tmin, tmax, vmin, vmax, ylim, data, color,
         else:
             ax.plot(times_, data_[ch_idx], color=color_)
 
-    if x_label is not None:
-        ax.set(xlabel=x_label)
-
-    if y_label is not None:
-        if isinstance(y_label, list):
-            ax.set_ylabel(y_label[ch_idx])
-        else:
-            ax.set_ylabel(y_label)
-
     def _format_coord(x, y, labels, ax):
         """Create status string based on cursor coordinates."""
         # find indices for datasets near cursor (if any)
         tdiffs = [np.abs(tvec - x).min() for tvec in times]
         nearby = [k for k, tdiff in enumerate(tdiffs) if
                   tdiff < (tmax - tmin) / 100]
-        timestr = '%6.3f s: ' % x
+        xlabel = ax.get_xlabel()
+        xunit = (xlabel[xlabel.find('(') + 1:xlabel.find(')')]
+                 if '(' in xlabel and ')' in xlabel else 's')
+        timestr = '%6.3f %s: ' % (x, xunit)
         if not nearby:
             return '%s Nothing here' % timestr
+        labels = [''] * len(nearby) if labels is None else labels
         nearby_data = [(data[n], labels[n], times[n]) for n in nearby]
         ylabel = ax.get_ylabel()
-        unit = (ylabel[ylabel.find('(') + 1:ylabel.find(')')]
-                if '(' in ylabel and ')' in ylabel else '')
-        labels = [''] * len(nearby_data) if labels is None else labels
+        yunit = (ylabel[ylabel.find('(') + 1:ylabel.find(')')]
+                 if '(' in ylabel and ')' in ylabel else '')
         # try to estimate whether to truncate condition labels
-        slen = 10 + sum([12 + len(unit) + len(label) for label in labels])
+        slen = 9 + len(xunit) + sum([12 + len(yunit) + len(label)
+                                     for label in labels])
         bar_width = (ax.figure.get_size_inches() * ax.figure.dpi)[0] / 5.5
         # show labels and y values for datasets near cursor
         trunc_labels = bar_width < slen
         s = timestr
         for data_, label, tvec in nearby_data:
             idx = np.abs(tvec - x).argmin()
-            s += '%7.2f %s' % (data_[ch_idx, idx], unit)
+            s += '%7.2f %s' % (data_[ch_idx, idx], yunit)
             if trunc_labels:
                 label = (label if len(label) <= 10 else
                          '%s..%s' % (label[:6], label[-2:]))
             s += ' [%s] ' % label if label else ' '
         return s
-
     ax.format_coord = lambda x, y: _format_coord(x, y, labels=labels, ax=ax)
 
     def _cursor_vline(event):
@@ -426,6 +421,15 @@ def _rm_cursor(event):
     ax.xaxis.label.set_color(hvline_color)
     ax.yaxis.label.set_color(hvline_color)
 
+    if x_label is not None:
+        ax.set_xlabel(x_label)
+
+    if y_label is not None:
+        if isinstance(y_label, list):
+            ax.set_ylabel(y_label[ch_idx])
+        else:
+            ax.set_ylabel(y_label)
+
     if vline:
         plt.axvline(vline, color=hvline_color, linewidth=1.0,
                     linestyle='--')
diff --git a/mne/viz/utils.py b/mne/viz/utils.py
--- a/mne/viz/utils.py
+++ b/mne/viz/utils.py
@@ -2332,6 +2332,7 @@ def _setup_ax_spines(axes, vlines, tmin, tmax, invert_y=False,
         axes.yaxis.set_label_coords(-0.05, 1 - ylabel_height
                                     if invert_y else ylabel_height)
 
+    vlines = [] if vlines is None else vlines
     xticks = sorted(list(set([x for x in axes.get_xticks()] + vlines)))
     axes.set_xticks(xticks)
     x_extrema = [t for t in xticks if tmax >= t >= tmin]


Test patch: