def test_plot_raw_child_figures(raw): """Test spawning and closing of child figures.""" raw.info['lowpass'] = 10. # allow heavy decim during plotting plt.close('all') # make sure we start clean assert len(plt.get_fignums()) == 0 fig = raw.plot() assert len(plt.get_fignums()) == 1 # test child fig toggles _child_fig_helper(fig, '?', 'fig_help') _child_fig_helper(fig, 'j', 'fig_proj') _child_fig_helper(fig, 'a', 'fig_annotation') assert len(fig.mne.child_figs) == 0 # make sure the helper cleaned up assert len(plt.get_fignums()) == 1 # test right-click → channel location popup fig.canvas.draw() _click_ch_name(fig, ch_index=2, button=3) assert len(fig.mne.child_figs) == 1 assert len(plt.get_fignums()) == 2 fig.mne.child_figs[0].canvas.key_press_event('escape') _close_event(fig.mne.child_figs[0]) assert len(plt.get_fignums()) == 1 # test right-click on non-data channel ix = raw.get_channel_types().index('ias') # find the shielding channel trace_ix = fig.mne.ch_order.tolist().index(ix) # get its plotting position assert len(fig.mne.child_figs) == 0 assert len(plt.get_fignums()) == 1 fig.canvas.draw() _click_ch_name(fig, ch_index=trace_ix, button=3) # should be no-op assert len(fig.mne.child_figs) == 0 assert len(plt.get_fignums()) == 1 # test resize of main window width, height = fig.canvas.manager.canvas.get_width_height() fig.canvas.manager.canvas.resize(width // 2, height // 2) plt.close('all')
def test_plot_raw_selection(raw): """Test selection mode of plot_raw().""" raw.info['lowpass'] = 10. # allow heavy decim during plotting plt.close('all') # ensure all are closed assert len(plt.get_fignums()) == 0 fig = raw.plot(group_by='selection', proj=False) assert len(plt.get_fignums()) == 2 sel_fig = fig.mne.fig_selection buttons = sel_fig.mne.radio_ax.buttons assert sel_fig is not None # test changing selection with arrow keys sel_dict = fig.mne.ch_selections assert len(fig.mne.traces) == len(sel_dict['Left-temporal']) # 6 sel_fig.canvas.key_press_event('down') assert len(fig.mne.traces) == len(sel_dict['Left-frontal']) # 3 sel_fig.canvas.key_press_event('down') assert len(fig.mne.traces) == len(sel_dict['Misc']) # 1 sel_fig.canvas.key_press_event('down') # ignored; no custom sel defined assert len(fig.mne.traces) == len(sel_dict['Misc']) # 1 # switch to butterfly mode sel_fig.canvas.key_press_event('b') assert len(fig.mne.traces) == len(np.concatenate(list(sel_dict.values()))) assert fig.mne.butterfly # test clicking on radio buttons → should cancel butterfly mode xy = buttons.circles[0].center _fake_click(sel_fig, sel_fig.mne.radio_ax, xy, xform='data') assert len(fig.mne.traces) == len(sel_dict['Left-temporal']) # 6 assert not fig.mne.butterfly # test clicking on "custom" when not defined: should be no-op before_state = buttons.value_selected xy = buttons.circles[-1].center _fake_click(sel_fig, sel_fig.mne.radio_ax, xy, xform='data') assert len(fig.mne.traces) == len(sel_dict['Left-temporal']) # unchanged assert buttons.value_selected == before_state # unchanged # test marking bad channel in selection mode → should make sensor red assert sel_fig.lasso.ec[:, 0].sum() == 0 # R of RGBA zero for all chans _click_ch_name(fig, ch_index=1, button=1) # mark bad assert sel_fig.lasso.ec[:, 0].sum() == 1 # one channel red _click_ch_name(fig, ch_index=1, button=1) # mark good assert sel_fig.lasso.ec[:, 0].sum() == 0 # all channels black # test lasso sel_fig._set_custom_selection() # lasso empty → should do nothing sensor_ax = sel_fig.mne.sensor_ax # Lasso with 1 mag/grad sensor unit (upper left) _fake_click(sel_fig, sensor_ax, (0, 1), xform='ax') _fake_click(sel_fig, sensor_ax, (0.65, 1), xform='ax', kind='motion') _fake_click(sel_fig, sensor_ax, (0.65, 0.7), xform='ax', kind='motion') _fake_click(sel_fig, sensor_ax, (0, 0.7), xform='ax', kind='release') want = ['MEG 0121', 'MEG 0122', 'MEG 0123'] assert sorted(want) == sorted(sel_fig.lasso.selection) # test joint closing of selection & data windows sel_fig.canvas.key_press_event(sel_fig.mne.close_key) _close_event(sel_fig) assert len(plt.get_fignums()) == 0
def test_plot_epochs_clicks(epochs, capsys): """Test plot_epochs mouse interaction.""" fig = epochs.plot(events=epochs.events) data_ax = fig.mne.ax_main x = fig.mne.traces[0].get_xdata()[3] y = fig.mne.traces[0].get_ydata()[3] n_epochs = len(epochs) epoch_num = fig.mne.inst.selection[0] # test (un)marking bad epochs _fake_click(fig, data_ax, [x, y], xform='data') # mark a bad epoch assert epoch_num in fig.mne.bad_epochs _fake_click(fig, data_ax, [x, y], xform='data') # unmark it assert epoch_num not in fig.mne.bad_epochs _fake_click(fig, data_ax, [x, y], xform='data') # mark it bad again assert epoch_num in fig.mne.bad_epochs # test vline fig.canvas.key_press_event('escape') # close and drop epochs _close_event(fig) # XXX workaround, MPL Agg doesn't trigger close event assert (n_epochs - 1 == len(epochs)) # test marking bad channels epochs = _get_epochs(None).load_data() # need more than 1 epoch this time fig = epochs.plot(n_epochs=3) data_ax = fig.mne.ax_main first_ch = data_ax.get_yticklabels()[0].get_text() assert first_ch not in fig.mne.info['bads'] _click_ch_name(fig, ch_index=0, button=1) # click ch name to mark bad assert first_ch in fig.mne.info['bads'] # test clicking scrollbars _fake_click(fig, fig.mne.ax_vscroll, [0.5, 0.5]) _fake_click(fig, fig.mne.ax_hscroll, [0.5, 0.5]) # test moving bad epoch offscreen fig.canvas.key_press_event('right') # move right x = fig.mne.traces[0].get_xdata()[-3] y = fig.mne.traces[0].get_ydata()[-3] _fake_click(fig, data_ax, [x, y], xform='data') # mark a bad epoch fig.canvas.key_press_event('left') # move back out, err = capsys.readouterr() assert 'out of bounds' not in out assert 'out of bounds' not in err fig.canvas.key_press_event('escape') _close_event(fig) # XXX workaround, MPL Agg doesn't trigger close event assert len(epochs) == 6 # test rightclick → image plot fig = epochs.plot() _click_ch_name(fig, ch_index=0, button=3) # show image plot assert len(fig.mne.child_figs) == 1 # test scroll wheel fig.canvas.scroll_event(0.5, 0.5, -0.5) # scroll down fig.canvas.scroll_event(0.5, 0.5, 0.5) # scroll up
def test_plot_ica_sources(raw_orig, mpl_backend): """Test plotting of ICA panel.""" raw = raw_orig.copy().crop(0, 1) picks = _get_picks(raw) epochs = _get_epochs() raw.pick_channels([raw.ch_names[k] for k in picks]) ica_picks = pick_types(raw.info, meg=True, eeg=False, stim=False, ecg=False, eog=False, exclude='bads') ica = ICA(n_components=2) ica.fit(raw, picks=ica_picks) ica.exclude = [1] fig = ica.plot_sources(raw) assert mpl_backend._get_n_figs() == 1 # change which component is in ICA.exclude (click data trace to remove # current one; click name to add other one) fig._redraw() # ToDo: This will be different methods in pyqtgraph x = fig.mne.traces[1].get_xdata()[5] y = fig.mne.traces[1].get_ydata()[5] fig._fake_click((x, y), xform='data') # exclude = [] _click_ch_name(fig, ch_index=0, button=1) # exclude = [0] fig._fake_keypress(fig.mne.close_key) fig._close_event() assert mpl_backend._get_n_figs() == 0 assert_array_equal(ica.exclude, [0]) # test when picks does not include ica.exclude. fig = ica.plot_sources(raw, picks=[1]) assert len(plt.get_fignums()) == 1 mpl_backend._close_all() # dtype can change int->np.int64 after load, test it explicitly ica.n_components_ = np.int64(ica.n_components_) # test clicks on y-label (need >2 secs for plot_properties() to work) long_raw = raw_orig.crop(0, 5) fig = ica.plot_sources(long_raw) assert len(plt.get_fignums()) == 1 fig._redraw() _click_ch_name(fig, ch_index=0, button=3) assert len(fig.mne.child_figs) == 1 assert len(plt.get_fignums()) == 2 # close child fig directly (workaround for mpl issue #18609) fig._fake_keypress('escape', fig=fig.mne.child_figs[0]) assert len(plt.get_fignums()) == 1 fig._fake_keypress(fig.mne.close_key) assert len(plt.get_fignums()) == 0 del long_raw # test with annotations orig_annot = raw.annotations raw.set_annotations(Annotations([0.2], [0.1], 'Test')) fig = ica.plot_sources(raw) assert len(fig.mne.ax_main.collections) == 1 assert len(fig.mne.ax_hscroll.collections) == 1 raw.set_annotations(orig_annot) # test error handling raw_ = raw.copy().load_data() raw_.drop_channels('MEG 0113') with pytest.raises(RuntimeError, match="Raw doesn't match fitted data"), \ pytest.warns(RuntimeWarning, match='could not be picked'): ica.plot_sources(inst=raw_) epochs_ = epochs.copy().load_data() epochs_.drop_channels('MEG 0113') with pytest.raises(RuntimeError, match="Epochs don't match fitted data"), \ pytest.warns(RuntimeWarning, match='could not be picked'): ica.plot_sources(inst=epochs_) del raw_ del epochs_ # test w/ epochs and evokeds ica.plot_sources(epochs) ica.plot_sources(epochs.average()) evoked = epochs.average() fig = ica.plot_sources(evoked) # Test a click ax = fig.get_axes()[0] line = ax.lines[0] _fake_click(fig, ax, [line.get_xdata()[0], line.get_ydata()[0]], 'data') _fake_click(fig, ax, [ax.get_xlim()[0], ax.get_ylim()[1]], 'data') # plot with bad channels excluded ica.exclude = [0] ica.plot_sources(evoked) # pretend find_bads_eog() yielded some results ica.labels_ = {'eog': [0], 'eog/0/crazy-channel': [0]} ica.plot_sources(evoked) # now with labels # pass an invalid inst with pytest.raises(ValueError, match='must be of Raw or Epochs type'): ica.plot_sources('meeow')
def test_plot_raw_traces(raw): """Test plotting of raw data.""" raw.info['lowpass'] = 10. # allow heavy decim during plotting events = _get_events() plt.close('all') # ensure all are closed fig = raw.plot(events=events, order=[1, 7, 5, 2, 3], n_channels=3, group_by='original') assert hasattr(fig, 'mne') # make sure fig.mne param object is present assert len(fig.axes) == 5 # setup x = fig.mne.traces[0].get_xdata()[5] y = fig.mne.traces[0].get_ydata()[5] data_ax = fig.mne.ax_main hscroll = fig.mne.ax_hscroll vscroll = fig.mne.ax_vscroll # test marking bad channels label = fig.mne.ax_main.get_yticklabels()[0].get_text() assert label not in fig.mne.info['bads'] _fake_click(fig, data_ax, [x, y], xform='data') # click data to mark bad assert label in fig.mne.info['bads'] _fake_click(fig, data_ax, [x, y], xform='data') # click data to unmark bad assert label not in fig.mne.info['bads'] _click_ch_name(fig, ch_index=0, button=1) # click name to mark bad assert label in fig.mne.info['bads'] # test other kinds of clicks _fake_click(fig, data_ax, [0.5, 0.999]) # click elsewhere (add vline) _fake_click(fig, data_ax, [0.5, 0.999], button=3) # remove vline _fake_click(fig, hscroll, [0.5, 0.5]) # change time _fake_click(fig, hscroll, [0.5, 0.5]) # shouldn't change time this time # test scrolling through channels labels = [label.get_text() for label in data_ax.get_yticklabels()] assert labels == [raw.ch_names[1], raw.ch_names[7], raw.ch_names[5]] _fake_click(fig, vscroll, [0.5, 0.01]) # change channels to end labels = [label.get_text() for label in data_ax.get_yticklabels()] assert labels == [raw.ch_names[5], raw.ch_names[2], raw.ch_names[3]] for _ in (0, 0): # first click changes channels to mid; second time shouldn't change _fake_click(fig, vscroll, [0.5, 0.5]) labels = [label.get_text() for label in data_ax.get_yticklabels()] assert labels == [raw.ch_names[7], raw.ch_names[5], raw.ch_names[2]] assert len(plt.get_fignums()) == 1 # test clicking a channel name in butterfly mode bads = fig.mne.info['bads'].copy() fig.canvas.key_press_event('b') _click_ch_name(fig, ch_index=0, button=1) # should be no-op assert fig.mne.info['bads'] == bads # unchanged fig.canvas.key_press_event('b') # test starting up in zen mode fig = plot_raw(raw, show_scrollbars=False) # test order, title, & show_options kwargs with pytest.raises(ValueError, match='order should be array-like; got'): raw.plot(order='foo') with pytest.raises(TypeError, match='title must be None or a string, got'): raw.plot(title=1) raw.plot(show_options=True) plt.close('all') # Color setting with pytest.raises(KeyError, match='must be strictly positive, or -1'): raw.plot(event_color={0: 'r'}) with pytest.raises(TypeError, match='event_color key must be an int, got'): raw.plot(event_color={'foo': 'r'}) annot = Annotations([10, 10 + raw.first_samp / raw.info['sfreq']], [10, 10], ['test', 'test'], raw.info['meas_date']) with pytest.warns(RuntimeWarning, match='outside data range'): raw.set_annotations(annot) fig = plot_raw(raw, events=events, event_color={-1: 'r', 998: 'b'}) plt.close('all') for group_by, order in zip(['position', 'selection'], [np.arange(len(raw.ch_names))[::-3], [1, 2, 4, 6]]): fig = raw.plot(group_by=group_by, order=order) x = fig.get_axes()[0].lines[1].get_xdata()[10] y = fig.get_axes()[0].lines[1].get_ydata()[10] _fake_click(fig, data_ax, [x, y], xform='data') # mark bad fig.canvas.key_press_event('down') # change selection _fake_click(fig, fig.get_axes()[2], [0.5, 0.5]) # change channels sel_fig = plt.figure(1) topo_ax = sel_fig.axes[1] _fake_click(sel_fig, topo_ax, [-0.425, 0.20223853], xform='data') fig.canvas.key_press_event('down') fig.canvas.key_press_event('up') fig.canvas.scroll_event(0.5, 0.5, -1) # scroll down fig.canvas.scroll_event(0.5, 0.5, 1) # scroll up _fake_click(sel_fig, topo_ax, [-0.5, 0.], xform='data') _fake_click(sel_fig, topo_ax, [0.5, 0.], xform='data', kind='motion') _fake_click(sel_fig, topo_ax, [0.5, 0.5], xform='data', kind='motion') _fake_click(sel_fig, topo_ax, [-0.5, 0.5], xform='data', kind='release') plt.close('all') # test if meas_date is off raw.set_meas_date(_dt_to_stamp(raw.info['meas_date'])[0]) annot = Annotations([1 + raw.first_samp / raw.info['sfreq']], [5], ['bad']) with pytest.warns(RuntimeWarning, match='outside data range'): raw.set_annotations(annot) with pytest.warns(None): # sometimes projection raw.plot(group_by='position', order=np.arange(8)) for fig_num in plt.get_fignums(): fig = plt.figure(fig_num) if hasattr(fig, 'radio'): # Get access to selection fig. break for key in ['down', 'up', 'escape']: fig.canvas.key_press_event(key) raw._data[:] = np.nan # this should (at least) not die, the output should pretty clearly show # that there is a problem so probably okay to just plot something blank with pytest.warns(None): raw.plot(scalings='auto') plt.close('all')
def test_plot_ica_sources(): """Test plotting of ICA panel.""" raw = read_raw_fif(raw_fname).crop(0, 1).load_data() picks = _get_picks(raw) epochs = _get_epochs() raw.pick_channels([raw.ch_names[k] for k in picks]) ica_picks = pick_types(raw.info, meg=True, eeg=False, stim=False, ecg=False, eog=False, exclude='bads') ica = ICA(n_components=2) ica.fit(raw, picks=ica_picks) ica.exclude = [1] fig = ica.plot_sources(raw) assert len(plt.get_fignums()) == 1 # change which component is in ICA.exclude (click data trace to remove # current one; click name to add other one) fig.canvas.draw() x = fig.mne.traces[1].get_xdata()[5] y = fig.mne.traces[1].get_ydata()[5] _fake_click(fig, fig.mne.ax_main, (x, y), xform='data') # exclude = [] _click_ch_name(fig, ch_index=0, button=1) # exclude = [0] fig.canvas.key_press_event(fig.mne.close_key) _close_event(fig) assert len(plt.get_fignums()) == 0 assert_array_equal(ica.exclude, [0]) # test when picks does not include ica.exclude. fig = ica.plot_sources(raw, picks=[1]) assert len(plt.get_fignums()) == 1 plt.close('all') # dtype can change int->np.int64 after load, test it explicitly ica.n_components_ = np.int64(ica.n_components_) # test clicks on y-label (need >2 secs for plot_properties() to work) long_raw = read_raw_fif(raw_fname).crop(0, 5).load_data() fig = ica.plot_sources(long_raw) assert len(plt.get_fignums()) == 1 fig.canvas.draw() _fake_click(fig, fig.mne.ax_main, (-0.1, 0), xform='data', button=3) assert len(fig.mne.child_figs) == 1 assert len(plt.get_fignums()) == 2 # close child fig directly (workaround for mpl issue #18609) fig.mne.child_figs[0].canvas.key_press_event('escape') assert len(plt.get_fignums()) == 1 fig.canvas.key_press_event(fig.mne.close_key) assert len(plt.get_fignums()) == 0 del long_raw # test with annotations orig_annot = raw.annotations raw.set_annotations(Annotations([0.2], [0.1], 'Test')) fig = ica.plot_sources(raw) assert len(fig.mne.ax_main.collections) == 1 assert len(fig.mne.ax_hscroll.collections) == 1 raw.set_annotations(orig_annot) # test error handling raw.info['bads'] = ['MEG 0113'] with pytest.raises(RuntimeError, match="Raw doesn't match fitted data"): ica.plot_sources(inst=raw) epochs.info['bads'] = ['MEG 0113'] with pytest.raises(RuntimeError, match="Epochs don't match fitted data"): ica.plot_sources(inst=epochs) epochs.info['bads'] = [] # test w/ epochs and evokeds ica.plot_sources(epochs) ica.plot_sources(epochs.average()) evoked = epochs.average() fig = ica.plot_sources(evoked) # Test a click ax = fig.get_axes()[0] line = ax.lines[0] _fake_click(fig, ax, [line.get_xdata()[0], line.get_ydata()[0]], 'data') _fake_click(fig, ax, [ax.get_xlim()[0], ax.get_ylim()[1]], 'data') # plot with bad channels excluded ica.exclude = [0] ica.plot_sources(evoked) ica.labels_ = dict(eog=[0]) ica.labels_['eog/0/crazy-channel'] = [0] ica.plot_sources(evoked) # now with labels with pytest.raises(ValueError, match='must be of Raw or Epochs type'): ica.plot_sources('meeow')