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

[GUI] Additional PyQt5 dependency? #103

Closed
adswa opened this issue Aug 19, 2022 · 2 comments · Fixed by #112
Closed

[GUI] Additional PyQt5 dependency? #103

adswa opened this issue Aug 19, 2022 · 2 comments · Fixed by #112

Comments

@adswa
Copy link

adswa commented Aug 19, 2022

Describe the bug

Invoking the GUI after an installation of mne-icalabel[gui] via pip on a Windows 11 and Debian bookworm machine in a fresh Python 3.9 environment fails with an

ImportError: Failed to import any qt binding

Installing PyQt5 resolves the error.

Steps to reproduce

On both machines, I performed the following installations:

pip install ipython
pip install mne-icalabel
pip install sklearn
pip install mne-icalabel[gui]

and then followed the tutorial exactly. Here is the outcome

click to expand Windows CMD
C:\Users\adina\repos>pip install mne-icalabel[gui]
Requirement already satisfied: mne-icalabel[gui] in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (0.3.1)
Requirement already satisfied: pooch in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne-icalabel[gui]) (1.6.0)
Requirement already satisfied: torch in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne-icalabel[gui]) (1.12.1)
Requirement already satisfied: mne>=1.1 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne-icalabel[gui]) (1.1.0)
Requirement already satisfied: numpy>=1.16.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne-icalabel[gui]) (1.23.2)
Requirement already satisfied: scipy>=1.2.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne-icalabel[gui]) (1.9.0)
Collecting qtpy
  Downloading QtPy-2.2.0-py3-none-any.whl (82 kB)
     |████████████████████████████████| 82 kB 2.9 MB/s
Collecting mne-qt-browser
  Downloading mne_qt_browser-0.3.1-py3-none-any.whl (70 kB)
     |████████████████████████████████| 70 kB 4.5 MB/s
Requirement already satisfied: matplotlib in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne-icalabel[gui]) (3.5.3)
Requirement already satisfied: decorator in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne>=1.1->mne-icalabel[gui]) (5.1.1)
Requirement already satisfied: jinja2 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne>=1.1->mne-icalabel[gui]) (3.1.2)
Requirement already satisfied: packaging in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne>=1.1->mne-icalabel[gui]) (21.3)
Requirement already satisfied: tqdm in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from mne>=1.1->mne-icalabel[gui]) (4.62.3)
Requirement already satisfied: appdirs>=1.3.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from pooch->mne-icalabel[gui]) (1.4.4)
Requirement already satisfied: requests>=2.19.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from pooch->mne-icalabel[gui]) (2.27.1)
Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from packaging->mne>=1.1->mne-icalabel[gui]) (3.0.9)
Requirement already satisfied: certifi>=2017.4.17 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from requests>=2.19.0->pooch->mne-icalabel[gui]) (2021.10.8)
Requirement already satisfied: charset-normalizer~=2.0.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from requests>=2.19.0->pooch->mne-icalabel[gui]) (2.0.7)
Requirement already satisfied: idna<4,>=2.5 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from requests>=2.19.0->pooch->mne-icalabel[gui]) (3.3)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from requests>=2.19.0->pooch->mne-icalabel[gui]) (1.26.7)
Requirement already satisfied: MarkupSafe>=2.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from jinja2->mne>=1.1->mne-icalabel[gui]) (2.1.1)
Requirement already satisfied: kiwisolver>=1.0.1 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from matplotlib->mne-icalabel[gui]) (1.4.4)
Requirement already satisfied: cycler>=0.10 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from matplotlib->mne-icalabel[gui]) (0.11.0)
Requirement already satisfied: pillow>=6.2.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from matplotlib->mne-icalabel[gui]) (9.2.0)
Requirement already satisfied: python-dateutil>=2.7 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from matplotlib->mne-icalabel[gui]) (2.8.2)
Requirement already satisfied: fonttools>=4.22.0 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from matplotlib->mne-icalabel[gui]) (4.36.0)
Requirement already satisfied: six>=1.5 in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from python-dateutil>=2.7->matplotlib->mne-icalabel[gui]) (1.16.0)
Collecting colorspacious
  Downloading colorspacious-1.1.2-py2.py3-none-any.whl (37 kB)
Collecting scooby
  Downloading scooby-0.6.0-py3-none-any.whl (14 kB)
Collecting qdarkstyle
  Downloading QDarkStyle-3.1-py2.py3-none-any.whl (870 kB)
     |████████████████████████████████| 870 kB 6.8 MB/s
Collecting darkdetect
  Downloading darkdetect-0.7.1-py2.py3-none-any.whl (8.2 kB)
Collecting pyqtgraph>=0.12.3
  Downloading pyqtgraph-0.12.4-py3-none-any.whl (995 kB)
     |████████████████████████████████| 995 kB 3.2 MB/s
Requirement already satisfied: typing-extensions in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from torch->mne-icalabel[gui]) (4.3.0)
Requirement already satisfied: colorama in c:\users\adina\appdata\local\programs\python\python39\lib\site-packages (from tqdm->mne>=1.1->mne-icalabel[gui]) (0.4.4)
Installing collected packages: qtpy, scooby, qdarkstyle, pyqtgraph, darkdetect, colorspacious, mne-qt-browser
Successfully installed colorspacious-1.1.2 darkdetect-0.7.1 mne-qt-browser-0.3.1 pyqtgraph-0.12.4 qdarkstyle-3.1 qtpy-2.2.0 scooby-0.6.0
WARNING: You are using pip version 21.2.4; however, version 22.2.2 is available.
You should consider upgrading via the 'C:\Users\adina\AppData\Local\Programs\Python\Python39\python.exe -m pip install --upgrade pip' command.

C:\Users\adina\repos>ipython
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import os
   ...:
   ...: import mne
   ...: from mne.preprocessing import ICA
   ...:
   ...: from mne_icalabel.gui import label_ica_components

In [2]: sample_data_folder = mne.datasets.sample.data_path()
   ...: sample_data_raw_file = os.path.join(
   ...:     sample_data_folder, "MEG", "sample", "sample_audvis_filt-0-40_raw.fif"
   ...: )
   ...: raw = mne.io.read_raw_fif(sample_data_raw_file)
   ...:
   ...: # Here we'll crop to 60 seconds and drop gradiometer channels for speed
   ...: raw.crop(tmax=60.0).pick_types(meg="mag", eeg=True, stim=True, eog=True)
   ...: raw.load_data()
Opening raw data file C:\Users\adina\mne_data\MNE-sample-data\MEG\sample\sample_audvis_filt-0-40_raw.fif...
    Read a total of 4 projection items:
        PCA-v1 (1 x 102)  idle
        PCA-v2 (1 x 102)  idle
        PCA-v3 (1 x 102)  idle
        Average EEG reference (1 x 60)  idle
    Range : 6450 ... 48149 =     42.956 ...   320.665 secs
Ready.
Reading 0 ... 9009  =      0.000 ...    59.999 secs...
Out[2]: <Raw | sample_audvis_filt-0-40_raw.fif, 171 x 9010 (60.0 s), ~14.8 MB, data loaded>

In [3]: # high-pass filter the data and then perform ICA
   ...: filt_raw = raw.copy().filter(l_freq=1.0, h_freq=None)
   ...: ica = ICA(n_components=15, max_iter="auto", random_state=97)
   ...: ica.fit(filt_raw)
Filtering raw data in 1 contiguous segment
Setting up high-pass filter at 1 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal highpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 1.00
- Lower transition bandwidth: 1.00 Hz (-6 dB cutoff frequency: 0.50 Hz)
- Filter length: 497 samples (3.310 sec)

[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
[Parallel(n_jobs=1)]: Done   1 out of   1 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=1)]: Done   2 out of   2 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=1)]: Done   3 out of   3 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=1)]: Done   4 out of   4 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=1)]: Done 161 out of 161 | elapsed:    0.0s finished
Fitting ICA to data using 161 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 0.8s.
Out[3]: <ICA | raw data decomposition, method: fastica (fit in 22 iterations on 9010 samples), 15 ICA components explaining 94.3 % of variance (161 PCA components available), channel types: mag, eeg, no sources marked for exclusion>

In [4]: gui = label_ica_components(raw, ica)
   ...:
   ...: # The `ica` object is modified to contain the component labels
   ...: # after closing the GUI and can now be saved
   ...: # gui.close()  # typically you close when done
   ...:
   ...: # Now, we can take a look at the components, which were modified in-place
   ...: # for the ICA instance.
   ...: print(ica.labels_)
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 gui = label_ica_components(raw, ica)
      3 # The `ica` object is modified to contain the component labels
      4 # after closing the GUI and can now be saved
      5 # gui.close()  # typically you close when done
      6
      7 # Now, we can take a look at the components, which were modified in-place
      8 # for the ICA instance.
      9 print(ica.labels_)

File ~\AppData\Local\Programs\Python\Python39\lib\site-packages\mne_icalabel\gui\__init__.py:26, in label_ica_components(inst, ica, show, block)
      5 """Launch the IC labelling GUI.
      6
      7 Parameters
   (...)
     22     The graphical user interface (GUI) window.
     23 """
     24 from mne.viz.backends._utils import _init_mne_qtapp, _qt_app_exec
---> 26 from ._label_components import ICAComponentLabeler
     28 # get application
     29 app = _init_mne_qtapp()

File ~\AppData\Local\Programs\Python\Python39\lib\site-packages\mne_icalabel\gui\_label_components.py:4, in <module>
      1 from typing import Dict, List, Union
      3 from matplotlib import pyplot as plt
----> 4 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
      5 from mne import BaseEpochs
      6 from mne.io import BaseRaw

File ~\AppData\Local\Programs\Python\Python39\lib\site-packages\matplotlib\backends\backend_qt5agg.py:7, in <module>
      4 from .. import backends
      6 backends._QT_FORCE_QT5_BINDING = True
----> 7 from .backend_qtagg import (    # noqa: F401, E402 # pylint: disable=W0611
      8     _BackendQTAgg, FigureCanvasQTAgg, FigureManagerQT, NavigationToolbar2QT,
      9     backend_version,  FigureCanvasAgg,  FigureCanvasQT
     10 )
     13 @_BackendQTAgg.export
     14 class _BackendQT5Agg(_BackendQTAgg):
     15     pass

File ~\AppData\Local\Programs\Python\Python39\lib\site-packages\matplotlib\backends\backend_qtagg.py:9, in <module>
      5 import ctypes
      7 from matplotlib.transforms import Bbox
----> 9 from .qt_compat import QT_API, _enum, _setDevicePixelRatio
     10 from .. import cbook
     11 from .backend_agg import FigureCanvasAgg

File ~\AppData\Local\Programs\Python\Python39\lib\site-packages\matplotlib\backends\qt_compat.py:142, in <module>
    140         break
    141     else:
--> 142         raise ImportError("Failed to import any qt binding")
    143 else:  # We should not get there.
    144     raise AssertionError(f"Unexpected QT_API: {QT_API}")

ImportError: Failed to import any qt binding
click to expand Linux zsh ``` (joss) adina@muninn in ~/repos/mne-icalabel on git:main! ❱ ipython Python 3.9.12 (main, Mar 24 2022, 13:02:21) Type 'copyright', 'credits' or 'license' for more information IPython 8.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import os
...:
...: import mne
...: from mne.preprocessing import ICA
...:
...: from mne_icalabel.gui import label_ica_components

In [2]: sample_data_folder = mne.datasets.sample.data_path()
...: sample_data_raw_file = os.path.join(
...: sample_data_folder, "MEG", "sample", "sample_audvis_filt-0-40_raw.fif"
...: )
...: raw = mne.io.read_raw_fif(sample_data_raw_file)
...:
...: # Here we'll crop to 60 seconds and drop gradiometer channels for speed
...: raw.crop(tmax=60.0).pick_types(meg="mag", eeg=True, stim=True, eog=True)
...: raw.load_data()
Opening raw data file /home/adina/mne_data/MNE-sample-data/MEG/sample/sample_audvis_filt-0-40_raw.fif...
Read a total of 4 projection items:
PCA-v1 (1 x 102) idle
PCA-v2 (1 x 102) idle
PCA-v3 (1 x 102) idle
Average EEG reference (1 x 60) idle
Range : 6450 ... 48149 = 42.956 ... 320.665 secs
Ready.
Reading 0 ... 9009 = 0.000 ... 59.999 secs...
Out[2]: <Raw | sample_audvis_filt-0-40_raw.fif, 171 x 9010 (60.0 s), ~14.8 MB, data loaded>

In [3]: # high-pass filter the data and then perform ICA
...: filt_raw = raw.copy().filter(l_freq=1.0, h_freq=None)
...: ica = ICA(n_components=15, max_iter="auto", random_state=97)
...: ica.fit(filt_raw)
Filtering raw data in 1 contiguous segment
Setting up high-pass filter at 1 Hz

FIR filter parameters

Designing a one-pass, zero-phase, non-causal highpass filter:

  • Windowed time-domain design (firwin) method
  • Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
  • Lower passband edge: 1.00
  • Lower transition bandwidth: 1.00 Hz (-6 dB cutoff frequency: 0.50 Hz)
  • Filter length: 497 samples (3.310 sec)

[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 0.0s remaining: 0.0s
[Parallel(n_jobs=1)]: Done 2 out of 2 | elapsed: 0.0s remaining: 0.0s
[Parallel(n_jobs=1)]: Done 3 out of 3 | elapsed: 0.0s remaining: 0.0s
[Parallel(n_jobs=1)]: Done 4 out of 4 | elapsed: 0.0s remaining: 0.0s
[Parallel(n_jobs=1)]: Done 161 out of 161 | elapsed: 0.1s finished
Fitting ICA to data using 161 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 0.5s.
Out[3]: <ICA | raw data decomposition, method: fastica (fit in 22 iterations on 9010 samples), 15 ICA components explaining 94.3 % of variance (161 PCA components available), channel types: mag, eeg, no sources marked for exclusion>

In [4]: gui = label_ica_components(raw, ica)
...:
...: # The ica object is modified to contain the component labels
...: # after closing the GUI and can now be saved
...: # gui.close() # typically you close when done
...:
...: # Now, we can take a look at the components, which were modified in-place
...: # for the ICA instance.
...: print(ica.labels_)

ImportError Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 gui = label_ica_components(raw, ica)
3 # The ica object is modified to contain the component labels
4 # after closing the GUI and can now be saved
5 # gui.close() # typically you close when done
6
7 # Now, we can take a look at the components, which were modified in-place
8 # for the ICA instance.
9 print(ica.labels_)

File ~/repos/mne-icalabel/mne_icalabel/gui/init.py:26, in label_ica_components(inst, ica, show, block)
5 """Launch the IC labelling GUI.
6
7 Parameters
(...)
22 The graphical user interface (GUI) window.
23 """
24 from mne.viz.backends._utils import _init_mne_qtapp, _qt_app_exec
---> 26 from ._label_components import ICAComponentLabeler
28 # get application
29 app = _init_mne_qtapp()

File ~/repos/mne-icalabel/mne_icalabel/gui/_label_components.py:4, in
1 from typing import Dict, List, Union
3 from matplotlib import pyplot as plt
----> 4 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
5 from mne import BaseEpochs
6 from mne.io import BaseRaw

File ~/env/joss/lib/python3.9/site-packages/matplotlib/backends/backend_qt5agg.py:7, in
4 from .. import backends
6 backends._QT_FORCE_QT5_BINDING = True
----> 7 from .backend_qtagg import ( # noqa: F401, E402 # pylint: disable=W0611
8 _BackendQTAgg, FigureCanvasQTAgg, FigureManagerQT, NavigationToolbar2QT,
9 backend_version, FigureCanvasAgg, FigureCanvasQT
10 )
13 @_BackendQTAgg.export
14 class _BackendQT5Agg(_BackendQTAgg):
15 pass

File ~/env/joss/lib/python3.9/site-packages/matplotlib/backends/backend_qtagg.py:9, in
5 import ctypes
7 from matplotlib.transforms import Bbox
----> 9 from .qt_compat import QT_API, _enum, _setDevicePixelRatio
10 from .. import cbook
11 from .backend_agg import FigureCanvasAgg

File ~/env/joss/lib/python3.9/site-packages/matplotlib/backends/qt_compat.py:142, in
140 break
141 else:
--> 142 raise ImportError("Failed to import any qt binding")
143 else: # We should not get there.
144 raise AssertionError(f"Unexpected QT_API: {QT_API}")

ImportError: Failed to import any qt binding

</details>


Installing PyQt5, which currently isn't listed as a dependency for the ```[gui]``` components, solved the issue on both machines. 
@mscheltienne
Copy link
Member

Yes, we took the same approach as mne-qt-browser: install qtpy but don't install one of the 4 possibles qt bindings. c.f. their requirement file. It is up to the user to install in the environment one of PyQt5, PyQt6, PySide2, or Pyside6.

The difference with mne-qt-browser is that we raise if the bindings are missing, while the browser will swap the backend to matplotlib.

The error message should be improved, it's not super explicit, and maybe also let's add a note in the tutorial. Would that be acceptable to resolve this issue?

@adswa
Copy link
Author

adswa commented Aug 19, 2022

The error message should be improved, it's not super explicit, and maybe also let's add a note in the tutorial. Would that be acceptable to resolve this issue?

sure. thanks for the delineation. :)

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 a pull request may close this issue.

2 participants