Repository: mne-tools/mne-python

Issue:
API: _Brain methods inconsistent between backends
#### Describe the bug
3D viz with pyvista has a `set_time_point` method but not a `set_time` method.  The opposite is true for 3D viz with mayavi backend.

#### Steps to reproduce
```python
import os
import mne

mne.viz.set_3d_backend('pyvista')  # or mayavi
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).crop(tmax=10)
raw.set_eeg_reference(projection=True)
events = mne.find_events(raw, stim_channel='STI 014')
event_dict = {'auditory/left': 1, 'auditory/right': 2}
epochs = mne.Epochs(raw, events, event_id=event_dict, preload=True)
evoked = epochs.average()
inverse_operator_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                     'sample_audvis-meg-oct-6-meg-inv.fif')
inv_operator = mne.minimum_norm.read_inverse_operator(inverse_operator_file)
stc = mne.minimum_norm.apply_inverse(evoked, inv_operator)
brain = stc.plot(subject='sample')
brain.set_time(0.5)  # or brain.set_time_point(0.5)
```

#### Expected results
the figure would update to show activity at time = 0.5 seconds

#### Actual results
`AttributeError: 'Brain' object has no attribute 'set_time'` (with pyvista)
`AttributeError: 'Brain' object has no attribute 'set_time_point'` (with mayavi)



Discussion:
Did you try `set_time_point()`?
nope. I was re-running an old script that had worked on 0.20.x (when mayavi was default) and failed on 0.21 (when pyvista is default)...  Testing now on master, I'm seeing that if the backend is set to mayavi, then only `set_time` works, and if set to pyvista, only `set_time_point` works.  The API should be backend-agnostic.
Agreed we should have `set_time`, I've had the same issue. @drammock can you open a PR to add it to `Brain`, then modify some test to use `set_time` instead of `set_time_point`?
>  @drammock can you open a PR to add it to `Brain`, then modify some test to use `set_time` instead of `set_time_point`?

sure, I'll tackle that later today or tomorrow morning.

Resolving patch:
diff --git a/mne/viz/_brain/_brain.py b/mne/viz/_brain/_brain.py
--- a/mne/viz/_brain/_brain.py
+++ b/mne/viz/_brain/_brain.py
@@ -2456,6 +2456,25 @@ def set_time_point(self, time_idx):
         self._data['time_idx'] = time_idx
         self._update()
 
+    def set_time(self, time):
+        """Set the time to display (in seconds).
+
+        Parameters
+        ----------
+        time : float
+            The time to show, in seconds.
+        """
+        if self._times is None:
+            raise ValueError(
+                'Cannot set time when brain has no defined times.')
+        elif min(self._times) <= time <= max(self._times):
+            self.set_time_point(np.interp(float(time), self._times,
+                                          np.arange(self._n_times)))
+        else:
+            raise ValueError(
+                f'Requested time ({time} s) is outside the range of '
+                f'available times ({min(self._times)}-{max(self._times)} s).')
+
     def _update_glyphs(self, hemi, vectors):
         from ..backends._pyvista import _set_colormap_range, _create_actor
         hemi_data = self._data.get(hemi)


Test patch:
diff --git a/mne/viz/_brain/tests/test_brain.py b/mne/viz/_brain/tests/test_brain.py
--- a/mne/viz/_brain/tests/test_brain.py
+++ b/mne/viz/_brain/tests/test_brain.py
@@ -166,6 +166,8 @@ def test_brain_init(renderer, tmpdir, pixel_ratio, brain_gc):
         brain.add_data(hemi_data, fmin=fmin, hemi=h, fmax=fmax,
                        colormap='hot', vertices=hemi_vertices,
                        smoothing_steps='nearest', colorbar=(0, 0), time=None)
+        with pytest.raises(ValueError, match='brain has no defined times'):
+            brain.set_time(0.)
         assert brain.data['lh']['array'] is hemi_data
         assert brain.views == ['lateral']
         assert brain.hemis == ('lh',)
@@ -173,6 +175,9 @@ def test_brain_init(renderer, tmpdir, pixel_ratio, brain_gc):
                        colormap='hot', vertices=hemi_vertices,
                        smoothing_steps=1, initial_time=0., colorbar=False,
                        time=[0])
+        with pytest.raises(ValueError, match='the range of available times'):
+            brain.set_time(7.)
+        brain.set_time(0.)
         brain.set_time_point(0)  # should hit _safe_interp1d
 
         with pytest.raises(ValueError, match='consistent with'):