Repository: mne-tools/mne-python

Issue:
Add title to plot_evoked_topomap
rebased version of https://github.com/mne-tools/mne-python/pull/1032

is there really no way to specify the size of the frame using pixels instead of relative to its size? The title frame comes out huge when there's only one topomap (this isn't something that I did, it's always been this way, just that there was a huge empty space at the top).



Discussion:

Resolving patch:
diff --git a/examples/plot_evoked_topomap.py b/examples/plot_evoked_topomap.py
--- a/examples/plot_evoked_topomap.py
+++ b/examples/plot_evoked_topomap.py
@@ -32,9 +32,9 @@
 # plot magnetometer data as topomaps
 evoked.plot_topomap(times, ch_type='mag')
 
-# add channel labels
-evoked.plot_topomap(0.1, ch_type='mag', show_names=True, colorbar=False,
-                    size=8)
-
 # plot gradiometer data (plots the RMS for each pair of gradiometers)
 evoked.plot_topomap(times, ch_type='grad')
+
+# add channel labels and title
+evoked.plot_topomap(0.1, ch_type='mag', show_names=True, colorbar=False,
+                    size=8, title='Auditory response')
diff --git a/mne/fiff/evoked.py b/mne/fiff/evoked.py
--- a/mne/fiff/evoked.py
+++ b/mne/fiff/evoked.py
@@ -399,7 +399,7 @@ def plot(self, picks=None, exclude='bads', unit=True, show=True, ylim=None,
     def plot_topomap(self, times=None, ch_type='mag', layout=None, vmax=None,
                      cmap='RdBu_r', sensors='k,', colorbar=True, scale=None,
                      unit=None, res=256, size=1, format="%3.1f", proj=False,
-                     show=True, show_names=False):
+                     show=True, show_names=False, title=None):
         """Plot topographic maps of specific time points
 
         Parameters
@@ -450,12 +450,14 @@ def plot_topomap(self, times=None, ch_type='mag', layout=None, vmax=None,
             passed, channel names will be formatted using the callable; e.g., to
             delete the prefix 'MEG ' from all channel names, pass the function
             lambda x: x.replace('MEG ', '')
+        title : str | None
+            Title. If None (default), no title is displayed.
         """
         return plot_evoked_topomap(self, times=times, ch_type=ch_type, layout=layout,
                                    vmax=vmax, cmap=cmap, sensors=sensors, 
                                    colorbar=colorbar, scale=scale, unit=unit,
                                    res=res, proj=proj, size=size, format=format,
-                                   show_names=show_names)
+                                   show_names=show_names, title=title)
 
     def to_nitime(self, picks=None):
         """Export Evoked object to NiTime
diff --git a/mne/viz.py b/mne/viz.py
--- a/mne/viz.py
+++ b/mne/viz.py
@@ -811,7 +811,7 @@ def plot_topo_image_epochs(epochs, layout=None, sigma=0.3, vmin=None,
 def plot_evoked_topomap(evoked, times=None, ch_type='mag', layout=None,
                         vmax=None, cmap='RdBu_r', sensors='k,', colorbar=True,
                         scale=None, unit=None, res=256, size=1, format='%3.1f',
-                        proj=False, show=True, show_names=False):
+                        proj=False, show=True, show_names=False, title=None):
     """Plot topographic maps of specific time points of evoked data
 
     Parameters
@@ -862,14 +862,17 @@ def plot_evoked_topomap(evoked, times=None, ch_type='mag', layout=None,
         passed, channel names will be formatted using the callable; e.g., to
         delete the prefix 'MEG ' from all channel names, pass the function
         lambda x: x.replace('MEG ', '')
+    title : str | None
+        Title. If None (default), no title is displayed.
     """
     import matplotlib.pyplot as plt
 
+    if ch_type.startswith('planar'):
+        key = 'grad'
+    else:
+        key = ch_type
+
     if scale is None:
-        if ch_type.startswith('planar'):
-            key = 'grad'
-        else:
-            key = ch_type
         scale = DEFAULTS['scalings'][key]
         unit = DEFAULTS['units'][key]
 
@@ -894,10 +897,10 @@ def plot_evoked_topomap(evoked, times=None, ch_type='mag', layout=None,
     n = len(times)
     nax = n + bool(colorbar)
     width = size * nax
-    height = size * 1. + max(0, 0.1 * (3 - size))
+    height = size * 1. + max(0, 0.1 * (4 - size))
     fig = plt.figure(figsize=(width, height))
     w_frame = plt.rcParams['figure.subplot.wspace'] / (2 * nax)
-    top_frame = max(.05, .2 / size)
+    top_frame = max((0.05 if title is None else 0.15), .2 / size)
     fig.subplots_adjust(left=w_frame, right=1 - w_frame, bottom=0,
                         top=1 - top_frame)
     time_idx = [np.where(evoked.times >= t)[0][0] for t in times]
@@ -915,8 +918,8 @@ def plot_evoked_topomap(evoked, times=None, ch_type='mag', layout=None,
     images = []
     for i, t in enumerate(times):
         plt.subplot(1, nax, i + 1)
-        tp = plot_topomap(data[:, i], pos, vmax=vmax, cmap=cmap, 
-                          sensors=sensors, res=res, names=names, 
+        tp = plot_topomap(data[:, i], pos, vmax=vmax, cmap=cmap,
+                          sensors=sensors, res=res, names=names,
                           show_names=show_names)
         images.append(tp)
         plt.title('%i ms' % (t * 1000))
@@ -942,6 +945,9 @@ def plot_evoked_topomap(evoked, times=None, ch_type='mag', layout=None,
                       plot_update_proj_callback=_plot_update_evoked_topomap)
         _draw_proj_checkbox(None, params)
 
+    if title is not None:
+        plt.suptitle(title, verticalalignment='top', size='x-large')
+        tight_layout(pad=2.0)
     if show:
         plt.show()
 


Test patch:
diff --git a/mne/tests/test_viz.py b/mne/tests/test_viz.py
--- a/mne/tests/test_viz.py
+++ b/mne/tests/test_viz.py
@@ -454,6 +454,18 @@ def test_plot_topomap():
         assert_true(all('MEG' not in x.get_text() for x in subplot.get_children()
                         if isinstance(x, matplotlib.text.Text)))
 
+        # Test title
+        def get_texts(p):
+            return [x.get_text() for x in p.get_children() if
+                    isinstance(x, matplotlib.text.Text)]
+
+        p = plot_evoked_topomap(evoked, times, ch_type='eeg')
+        assert_equal(len(get_texts(p)), 0)
+        p = plot_evoked_topomap(evoked, times, ch_type='eeg', title='Custom')
+        texts = get_texts(p)
+        assert_equal(len(texts), 1)
+        assert_equal(texts[0], 'Custom')
+
         with warnings.catch_warnings(record=True):  # delaunay triangulation warning
             plot_evoked_topomap(evoked, times, ch_type='mag', layout='auto')
         assert_raises(RuntimeError, plot_evoked_topomap, evoked, 0.1, 'mag',