Repository: mne-tools/mne-python

Issue:
ENH: show channel in plot_evoked_topo()
In the `plot_evoked_topo` display, the status bar could show which channel you are hovering over. Currently if you want to zoom in to e.g. MEG 1711 you probably will have no idea where it is (unless you are intimately familiar with the sensor layout).




Discussion:
That would be great. Usually I click on the channel to have the window pop up and get the name from there, but it's not as convenient as a status bar update would be.

Resolving patch:
diff --git a/mne/viz/topo.py b/mne/viz/topo.py
--- a/mne/viz/topo.py
+++ b/mne/viz/topo.py
@@ -91,6 +91,25 @@ def _iter_topography(info, layout, on_pick, fig, fig_facecolor='k',
     if fig is None:
         fig = plt.figure()
 
+    def format_coord_unified(x, y, pos=None, ch_names=None):
+        """Update status bar with channel name under cursor."""
+        # find candidate channels (ones that are down and left from cursor)
+        pdist = np.array([x, y]) - pos[:, :2]
+        pind = np.where((pdist >= 0).all(axis=1))[0]
+        if len(pind) > 0:
+            # find the closest channel
+            closest = pind[np.sum(pdist[pind, :]**2, axis=1).argmin()]
+            # check whether we are inside its box
+            in_box = (pdist[closest, :] < pos[closest, 2:]).all()
+        else:
+            in_box = False
+        return (('%s (click to magnify)' % ch_names[closest]) if
+                in_box else 'No channel here')
+
+    def format_coord_multiaxis(x, y, ch_name=None):
+        """Update status bar with channel name under cursor."""
+        return '%s (click to magnify)' % ch_name
+
     fig.set_facecolor(fig_facecolor)
     if layout is None:
         layout = find_layout(info)
@@ -111,7 +130,10 @@ def _iter_topography(info, layout, on_pick, fig, fig_facecolor='k',
             under_ax.axis('off')
         else:
             under_ax = axes
+        under_ax.format_coord = partial(format_coord_unified, pos=pos,
+                                        ch_names=layout.names)
         under_ax.set(xlim=[0, 1], ylim=[0, 1])
+
         axs = list()
     for idx, name in iter_ch:
         ch_idx = ch_names.index(name)
@@ -126,6 +148,7 @@ def _iter_topography(info, layout, on_pick, fig, fig_facecolor='k',
             ax._mne_ch_name = name
             ax._mne_ch_idx = ch_idx
             ax._mne_ax_face_color = axis_facecolor
+            ax.format_coord = partial(format_coord_multiaxis, ch_name=name)
             yield ax, ch_idx
         else:
             ax = Bunch(ax=under_ax, pos=pos[idx], data_lines=list(),
@@ -467,7 +490,7 @@ def _plot_timeseries_unified(bn, ch_idx, tmin, tmax, vmin, vmax, ylim, data,
     for data_, color_ in zip(data, color):
         data_lines.append(ax.plot(
             bn.x_t + bn.x_s * times, bn.y_t + bn.y_s * data_[ch_idx],
-            color=color_, clip_on=True, clip_box=pos)[0])
+            linewidth=0.5, color=color_, clip_on=True, clip_box=pos)[0])
     if vline:
         vline = np.array(vline) * bn.x_s + bn.x_t
         ax.vlines(vline, pos[1], pos[1] + pos[3], color=hvline_color,


Test patch:
diff --git a/mne/viz/tests/test_topo.py b/mne/viz/tests/test_topo.py
--- a/mne/viz/tests/test_topo.py
+++ b/mne/viz/tests/test_topo.py
@@ -128,6 +128,8 @@ def return_inds(d):  # to test function kwarg to zorder arg of evoked.plot
     plt.close('all')
     for ax, idx in iter_topography(evoked.info):
         ax.plot(evoked.data[idx], color='red')
+        # test status bar message
+        assert_true(evoked.ch_names[idx] in ax.format_coord(.5, .5))
     plt.close('all')
 
 
@@ -136,6 +138,9 @@ def test_plot_topo_single_ch():
     import matplotlib.pyplot as plt
     evoked = _get_epochs().average()
     fig = plot_evoked_topo(evoked, background_color='w')
+    # test status bar message
+    ax = plt.gca()
+    assert_true('MEG 0113' in ax.format_coord(.065, .63))
     num_figures_before = len(plt.get_fignums())
     _fake_click(fig, fig.axes[0], (0.08, 0.65))
     assert_equal(num_figures_before + 1, len(plt.get_fignums()))