mne-python icon indicating copy to clipboard operation
mne-python copied to clipboard

Enh browserfig UI events

Open nmarkowitz opened this issue 1 year ago • 9 comments

What does this implement/fix?

Creates hook to use UI-events in the Qt browser. Also implements the TimeChange event for the matplotlib-based browser.

Additional information

nmarkowitz avatar Aug 28 '24 13:08 nmarkowitz

This needs a unit test.

wmvanvliet avatar Aug 28 '24 13:08 wmvanvliet

Now the following works:

import mne

path = mne.datasets.sample.data_path()
raw = mne.io.read_raw_fif(path / "MEG/sample/sample_audvis_raw.fif", preload=True)

mne.viz.set_browser_backend("matplotlib")
fig1 = raw.copy().pick("eeg").plot()
fig2 = raw.copy().pick("meg").plot()

mne.viz.ui_events.link(fig1, fig2)

And scrolling through time will update both figures.

wmvanvliet avatar Aug 28 '24 13:08 wmvanvliet

@wmvanvliet I added the ChannelBrowse and TimeBrowse ui events. Just need to make unit tests for them

nmarkowitz avatar Aug 31 '24 21:08 nmarkowitz

This needs a unit test.

Where/how would I put a unit test? I also saw there's no unit test for Brain and EvokedField objects. Do those need unit tests as well?

nmarkowitz avatar Aug 31 '24 22:08 nmarkowitz

@wmvanvliet the matplotlib and qt browsers (on this branch https://github.com/mne-tools/mne-qt-browser/pull/286 ) are synced in time and channels. It's also working for epochs.

nmarkowitz avatar Sep 06 '24 17:09 nmarkowitz

I've overhauled the event code in the matplotlib figure a bit. The biggest improvement is that now the TimeBrowse event generates meaningful time_start and time_end values. So if you open a raw browser and an epochs browser side-by-side, you can link the event streams and have their scrolling behavior synced. Try this out:

import mne
path = mne.datasets.sample.data_path()
raw = mne.io.read_raw_fif(path / "MEG" / "sample" / "sample_audvis_raw.fif")
events = mne.find_events(raw)
epochs = mne.Epochs(raw, events, preload=True)
mne.viz.set_browser_backend('matplotlib')

fig1 = raw.plot(events=events)
fig2 = epochs.plot()

def on_time_browse_event(event):
    print(event)

def on_channel_browse_event(event):
    print(event)

def on_time_change_event(event):
    print(event)

mne.viz.ui_events.subscribe(fig2, 'time_browse', on_time_browse_event)
mne.viz.ui_events.subscribe(fig2, 'channel_browse', on_channel_browse_event)
mne.viz.ui_events.subscribe(fig2, 'time_change', on_time_change_event)
mne.viz.ui_events.link(fig2, fig1, include_events=['time_browse', 'channel_browse'])

wmvanvliet avatar Sep 10 '24 09:09 wmvanvliet

another improvement is that the figure is no longer updated twice on every keypress. Before, the keypress first updated the figure and then generated the UI event, which caused the event handler to update the figure again. Now, the keypress just generates the UI event and lets the event handler take care of updating the figure.

wmvanvliet avatar Sep 10 '24 09:09 wmvanvliet

Should we do the same for the vertical bar? Currently when the vertical bar appears in epoch mode all epochs at that timepoint are marked with a vertical bar. So raw time doesn't matter here.

nmarkowitz avatar Sep 10 '24 19:09 nmarkowitz

I've updated the TimeChange event (the vertical bar) to properly indicate the time in the epoch (from tmin to tmax instead of from 0 to epoch duration)

wmvanvliet avatar Sep 11 '24 04:09 wmvanvliet

@wmvanvliet @drammock I need to finish this branch and pull this before I can pull https://github.com/mne-tools/mne-qt-browser/pull/286 . Currently there's a bug when changing channels that results in incorrect labels

nmarkowitz avatar Jun 23 '25 20:06 nmarkowitz

Currently there's a bug when changing channels that results in incorrect labels

So this is a point at which I would start adding stuff like this to the code:

logger.debug(f"Channel change event received by {self}: {ch_names[0]=} {ch_names[-1]=}")

and similarly for "Channel change event emitted by ..." etc. Then you should see in code something that looks correct, but then the labeling be incorrect or so. Then you can add a logger.debug statement where the channels are actually relabeled in the matplotlib browser code, etc.

EDIT: And you'll need to use a mne.set_log_level("debug") to see the messages

larsoner avatar Jun 24 '25 19:06 larsoner