Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Draft GUI for labeling components #66

Merged
merged 60 commits into from
Jul 29, 2022
Merged

Conversation

adam2392
Copy link
Member

@adam2392 adam2392 commented Jun 15, 2022

PR Description

Addresses: #12

From MNE-Python, I understand there are two parts of a GUI API pattern that we can follow:

I'm not very familiar with a good workflow for getting this to work properly so can definitely use help :p

Details of the GUI

The GUI will I think house 3 plots, stemming from ICA.plot_properties() (https://mne.tools/stable/generated/mne.preprocessing.ICA.html#mne.preprocessing.ICA.plot_properties). There should be basic saving/loading functions of course that interface with the BIDS files on disc.

Merge checklist

Maintainer, please confirm the following before merging:

  • All comments resolved
  • This is not your own PR
  • All CIs are happy
  • PR title starts with [MRG]
  • whats_new.rst is updated
  • PR description includes phrase "closes <#issue-number>"

@codecov
Copy link

codecov bot commented Jun 15, 2022

Codecov Report

Merging #66 (b3e2f1f) into main (c767cac) will decrease coverage by 5.94%.
The diff coverage is 72.56%.

@@            Coverage Diff             @@
##             main      #66      +/-   ##
==========================================
- Coverage   96.58%   90.63%   -5.95%     
==========================================
  Files          19       23       +4     
  Lines         673      897     +224     
==========================================
+ Hits          650      813     +163     
- Misses         23       84      +61     
Impacted Files Coverage Δ
mne_icalabel/commands/mne_gui_ic_annotation.py 0.00% <0.00%> (ø)
mne_icalabel/conftest.py 100.00% <ø> (+2.94%) ⬆️
mne_icalabel/utils/_checks.py 46.42% <21.05%> (-53.58%) ⬇️
mne_icalabel/gui/_label_components.py 86.11% <86.11%> (ø)
mne_icalabel/gui/__init__.py 88.88% <88.88%> (ø)
mne_icalabel/iclabel/label_components.py 95.65% <92.85%> (-4.35%) ⬇️
mne_icalabel/__init__.py 100.00% <100.00%> (ø)
mne_icalabel/annotation/bids.py 85.24% <100.00%> (+3.24%) ⬆️
mne_icalabel/config.py 100.00% <100.00%> (ø)
mne_icalabel/utils/__init__.py 100.00% <100.00%> (ø)
... and 1 more

Help us with your feedback. Take ten seconds to tell us how you rate us.

Copy link
Member

@mscheltienne mscheltienne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was not what I had in mind ahah I was thinking of a GUI that loads components one by one, not necessarily from the same (raw, ica). But that fits way better in the API of this package, so I like it!
I'll add a second file with a second iteration to this PR, I got some time today :)

mne_icalabel/gui/__init__.py Outdated Show resolved Hide resolved
mne_icalabel/gui/_label_components.py Outdated Show resolved Hide resolved
mne_icalabel/gui/_label_components.py Outdated Show resolved Hide resolved
mne_icalabel/gui/_label_components.py Outdated Show resolved Hide resolved
mne_icalabel/gui/_label_components.py Outdated Show resolved Hide resolved
Comment on lines 56 to 67
def _make_ts_plot(width=4, height=4, dpi=300):
"""Make subplot for the component time-series."""
fig = Figure(figsize=(width, height), dpi=dpi)
canvas = FigureCanvas(fig)
ax = fig.subplots()
fig.subplots_adjust(bottom=0, left=0, right=1, top=1, wspace=0, hspace=0)
ax.set_facecolor("k")
# clean up excess plot text, invert
ax.invert_yaxis()
ax.set_xticks([])
ax.set_yticks([])
return canvas, fig
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the time-series plot, we need to be able to scroll in time, and probably also zoom on both the X and Y-axis.
How about maybe looking into the mne-qt-browser, maybe we can just load the widgets it has?

mne_icalabel/gui/_label_components.py Outdated Show resolved Hide resolved
@adam2392
Copy link
Member Author

@mscheltienne FYI, see mne-tools/mne-qt-browser#117

where we can just embed as needed. Seems super simple.

@adam2392
Copy link
Member Author

@mscheltienne sorry I haven't gotten around to this yet, but perhaps we can do a pair session this week on getting this up and running?

I pushed up some fixes to at least let it compile.

@mscheltienne
Copy link
Member

Same it was on my mind for a while.. but I was lacking the time last week. I can work on this on Wednesday or Thursday.
We can do a pair session if you want.

@mscheltienne
Copy link
Member

My week filled up very quickly.. I'm sorry but I don't think I can work on it this week, it's all yours. I can have a look this weekend maybe.

@adam2392
Copy link
Member Author

Oh nice! That works, except for some OS/Python combinations it seems. I'm guessing that setting up the GUI to be robust across CI testing is non-trivial...

Does MNE-Python only test GUI on certain OS/Python combos?

@larsoner
Copy link
Member

We run GUI tests on all OSes and Python versions. On Linux you need xvfb but on Windows and macOS you only need something extra if you need OpenGL support. Regular windowing and matplotlib should be fine on those OSes without any issues I think

@adam2392
Copy link
Member Author

Screen Shot 2022-07-21 at 10 40 30 AM

I still end up getting this weird visual output. If I run things with the entrypoint, then I get this resulting weird visual.

Interestingly, it shows up this way on @ayoun25 Windows machine too, so I wonder if this is a version issue?...

Screen Shot 2022-07-25 at 9 31 43 PM

@adam2392
Copy link
Member Author

We run GUI tests on all OSes and Python versions. On Linux you need xvfb but on Windows and macOS you only need something extra if you need OpenGL support. Regular windowing and matplotlib should be fine on those OSes without any issues I think

Swell! I think now the CI is working! black magic...

@adam2392
Copy link
Member Author

Notes from today:

  • seems like the plot_sources is plotting twice (so first off set show=False where needed), which is fixed if we explicitly instantiate the widget.
  • however, then the button clicking is not registering

@mscheltienne
Copy link
Member

I did not have as much time to dig in as I was hopping, but the issue is that every time you click on a new IC to display, a new browser is opened and the old one is not deleted. It actually stays.. behind.
I tried different ideas with little success:

  • layout.removeItem()
  • layout.removeWidget()
  • browser.close()
  • browser.closeEvent()

This issue will be gone once we can open an initial browser and swap the instance displayed via a method supported by the browser, but for now, I still did not find how to delete/close the old browsers. @marsipu by any chance, do you know how from the top of your head?

I'll keep looking.

@mscheltienne
Copy link
Member

Also, this is not macOS specific, even on my windows PC it does appear (the one I used for the quick screen recording), once you start resizing the window. So I think you are right @adam2392, it showed up on your computer and not on mine because of the resolution/monitor.

@mscheltienne
Copy link
Member

mscheltienne commented Jul 28, 2022

@adam2392 Give it a shot, I think it's working correctly on my mac.
My hypothesis is that the C++ reference was not removed, thus the Widget was not removed even if it was detached from the layout. Setting the parent to None when replacing the browser seems to do the trick:

timeSeries_widget = self.ica.plot_sources(self.inst, picks=[self.selected_component])
self._central_widget.layout().replaceWidget(self._timeSeries_widget, timeSeries_widget)
self._timeSeries_widget.setParent(None)
self._timeSeries_widget = timeSeries_widget

@adam2392
Copy link
Member Author

adam2392 commented Jul 28, 2022

@mscheltienne nice! Now I get this. I noticed there is no way of getting rid of the plot for the EOG channels (see https://github.com/mne-tools/mne-python/blob/9e4a0b492299d3638203e2e6d2264ea445b13ac0/mne/viz/ica.py#L998-L1006). Do we want to keep it in there? If not, we prolly need to PR the plot_sources function.

If so, I think we can merge this!

Screen Shot 2022-07-28 at 9 24 34 AM

set_browser_backend("qt") # force MNE to use the QT Browser

# keep an internal pointer to the instance and to the ICA
self._inst = inst
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adam2392 We could drop the EOG / ECG channels here. We don't need them anyway for this GUI.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this would modify the outside instance in place. Perhaps we leave it for now? It is kind of nice if you have it to compare against time-series traces that look "like it".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you want, we can always do a copy before dropping the channels.
It's True that it is a nice piece of information to have to compare against the IC's time-series. The main issue IMO is the size of that browser and especially of the scrollbar/statusbar.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just merge this for now for the sake of giving Aaron something to work with. We can modify as we go.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

@alexrockhill
Copy link

This is a bit longer-term but I think it's worth cross referencing to mne-tools/mne-python#10913 for the new backend abstraction that I wrote in case that makes things easier down the road with the abstraction making things cleaner and or adding notebook support. Looks great by the way!

@adam2392
Copy link
Member Author

@alexrockhill thanks! That's a big PR.

What's a good summary? :p

Is it that we can create the GUIs using the abstract interface? It buys us automatically notebook support?

@alexrockhill
Copy link

@alexrockhill thanks! That's a big PR.

What's a good summary? :p

Is it that we can create the GUIs using the abstract interface? It buys us automatically notebook support?

The summary is that it is free notebook support and implements all the widgets in a way that looks similar between Qt and notebook with the standard object-oriented design (e.g. HBox, Buttons etc.).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants