mne-python
mne-python copied to clipboard
Make full installation depend on PySide6 by default
Since the bug with interactive plotting when using PySidd6 has now been fixed, we should be able to replace the PyQt6 dependency in mne[full] with PySide6.
- PySide6 is the official Qt for Python binding.
- Binary wheels of PySide6 are available for more platforms, including
aarch64. - The conda-based installation – which is what we recommend in our docs – and our installers will use PySide6 starting with the next MNE-Python release.
- PySide6 is available from conda-forge, while PyQt6 is not (yet). Switching the dependency to PySide6 will make for a cleaner and smoother experience in situations where a conda-forge and PyPI-based installation of MNE-Python are mixed.
The abovementioned bug fix should be included in the upcoming release 6.7.3.
Lastly, probably more controversial, but: my opinion is that once we've made the switch, we can (should) drop support for PyQt and get rid of qtpy, which just adds another layer of complexity that won't be needed anymore: PySide is the official binding, has a more permissive license, and is more actively developed.
I agree :100:%! Once PySide6 is available and works, qtpy is obsolete because there is no need to support another Qt binding such as PyQt.
There are two points in favor of PySide6 that I'd like to highlight more explicity:
- PySide6 is licensed under the LGPL, whereas PyQt6 uses the GPL. It's not entirely safe to use GPL-licensed code in a BSD- or MIT-licensed project.
- PySide6 is being updated to the latest Qt versions much more rapidly and frequently (probably because it is the official binding). For example, PyQt6 has been stuck at 6.7.0 for months now, whereas PySide6 closely follows the Qt release cycle.
Okay with switching to a PySide6>=6.7.3 pin once it actually lands.
I'd rather not get rid of qtpy -- it has very little maintenance overhead / burden for us and makes our library compatible with more Qt bindings, including PyQt5 which is still widely used on conda-forge. Maybe in a year or two once things settle it could make sense to switch to PySide6, but I don't think we should move fast on this.
I think people should not be using an old Qt binding, and dropping qtpy would ensure that. I understand if you don't want to do it right now, but we should eventually get rid of it (a year or two seems too long, let's maybe reconsider in half a year or so).
(on a somewhat related side node I always found it extremely funny that it's the Spyder devs who came up with qtpy, yet not only does Spyder explicitly depend on PyQt5, but even on a very specific version of it -- in addition to qtpy. I find this ironic and it kind of impairs my trust in the approach qtpy has taken -- it seems not even its own devs trust it enough to do the job full and well.)
-1 from me on dropping qtpy because I would like Qt5 support a bit longer. On our workstations in our department, we don't update our entire anaconda environment that fast and the machines used by our researcher to get their daily work done are still on Qt5 (because Qt6 is not available yet through conda-forge).
Qt6 is available through conda-forge via PySide6.
Yes Qt6 has been available on conda-forge for almost 2 years now. Qt6 is more than 3.5 years old.
Honest question, if the workstations don't get updated anyway – can you not just stick with an older MNE-Python, then?
That said I don't feel strongly about qtpy, so for now i'm rather neutral on this one. I just think it's one bit we should definitely consider getting rid of whenever we believe it's convenient!
Yes Qt6 has been available on conda-forge for almost 2 years now. Qt6 is more than 3.5 years old.
Honest question, if the workstations don't get updated anyway – can you not just stick with an older MNE-Python, then?
wow I feel silly. I always thought pyqt6 would be the official package, but it is pyside6 and that has been available for a long time. I never knew. The solver always refused to install pyside6 because we are also users of Spyder, which depends on pyqt5... This is an informative thread for me.
Yes Spyder is super odd, they do use qtpy but depend on PyQt5 😵💫 And I don't think you're the first one who believes PyQt6 is the official binding. In fact I would bet the majority of Python users would answer this question incorrectly. It's such a mess on many levels. PyQt6 simply has the much better name.
FYI I just tried to use PyQt6 and then PySide6 with vscode's Python debugger and ran into https://github.com/microsoft/debugpy/issues/1488 and https://github.com/microsoft/debugpy/pull/1569. So Qt6 support is still in the works / incomplete in VSCode in addition to conda and Spyder apparently!
Looks like @wmvanvliet has been busy trying to make things better there at least :)
https://github.com/microsoft/debugpy/commit/0035e83a40ba77080f9ae8b829835b2a0c4834f6
I'd like to revive this discussion, mainly because installing mne with just its core dependencies (i.e., pip install mne or corresponding uv command) provides a working interactive Matplotlib backend only on macOS, but not on Windows or Linux. This means that plotting does not work out of the box on these two platforms, and at least on Linux does not provide a useful error message.
import mne
import numpy as np
data = np.random.randn(2, 1000)
info = mne.create_info(2, 1000, "eeg")
raw = mne.io.RawArray(data, info)
raw.plot()
The plot does not show on Linux, even when explicitly setting block=True (show=True by default). There is also no error message. Only when using plt.show() (with an additional import matplotlib.pyplot as plt) do I get the following message (no error, just a warning):
UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
I haven't tested this on Windows, but I'm assuming the behavior will be similar.
Therefore, I'd really like to suggest again that we include pyside6 as part of the core dependencies, so that pip install mne results in working interactive plots on all platforms.
Note that meanwhile, PySide6 has resolved the long-standing issue regarding interactive plots:
https://bugreports.qt.io/browse/PYSIDE-2192
So I think we could just require PySide6 ≥ 6.8 to be safe.
I consider this more or less a matplotlib issue. I'd rather follow whatever they do and stay consistent with scientific Python ecosystem practices. So if pip install matplotlib doesn't install a Qt binding, then pip install mne probably shouldn't either.
I think there's a significant difference here. MNE automatically shows interactive plots, which Matplotlib doesn't. So unless we change that behavior (which I have suggested previously) to be consistent with Matplotlib, I don't think we should then also just follow what Matplotlib is doing.
PS: There is a good reason why Matplotlib doesn't install an interactive backend by default, simply because there are use cases that do not require it. On the other hand, MNE absolutely requires interactive plots.
MNE automatically shows interactive plots, which Matplotlib doesn't. So unless we change that behavior (which I have suggested previously) to be consistent with Matplotlib, I don't think we should then also just follow what Matplotlib is doing.
I see what you're saying but I am not totally convinced by this. Yes it's a little inconsistent that we have show=True and matplotlib has show=False but even when you do plt.show() in matplotlib it emits a warning, so we're consistent at least in what the user sees, even though we have a different default attempted behavior. The show in both places basically means "try to show it if you can / are in a framework that allows it". So I don't think our show=True default is a convincing reason to deviate from what matplotlib does / recommends / installs.
PS: There is a good reason why Matplotlib doesn't install an interactive backend by default, simply because there are use cases that do not require it. On the other hand, MNE absolutely requires interactive plots.
I agree in principle this would be a reason to potentially deviate from matplotlib. However, I don't think all use cases for MNE absolutely require interactive plots. There are existing use cases in the wild where Qt is not required, and beyond that where interactive viz of any type isn't required. For interactive viz without Qt, we have supported for some years notebook/Jupyter-only workflows for example where everything is done in a notebook rendered server-side and viewed client-side in a notebook, and installing Qt in those situations has even been a bit problematic and makes the situation harder to manage for end users. For use cases without viz at all, there are folks who use MNE for lower-level operations (maybe just file I/O!) or entire preprocessing pipelines on servers for example without ever wanting to visualize on the machine where mne gets installed and does the work. So while there is a big subset of MNE users who would want Qt viz out of the box, I don't think it makes sense to make it a core dep and thereby force installation of PySide6 upon every end-user who installs MNE and every downstream package that depends on MNE.
I see what you're saying but I am not totally convinced by this. Yes it's a little inconsistent that we have
show=Trueand matplotlib hasshow=Falsebut even when you doplt.show()in matplotlib it emits a warning, so we're consistent at least in what the user sees, even though we have a different default attempted behavior. Theshowin both places basically means "try to show it if you can / are in a framework that allows it". So I don't think ourshow=Truedefault is a convincing reason to deviate from what matplotlib does / recommends / installs.
That's not entirely correct. While the user sees a warning when doing plt.show(), there is absolutely nothing when doing raw.plot(show=True, block=True). I think this needs to be fixed regardless of making PySide6 a core dependency or not.
Regarding your comments on use cases, to me interactive plotting is something that 95% of all users will want to do almost immediately. I do not have any numbers to back this up, but from my experience and talking to other people, I think this estimate is not far off. Currently, those users who did pip install mne will not see a plot, no matter what they do. They have to manually pip install pyside6. If you are not willing to make PySide6 a core dependency, then there should at least be some warning/error when plotting and no interactive backend is available. But again, this means that most users will have to pip install mne pyside6 for basic functionality!
I understand your point about people wanting a minimal subset of functionality, but then I suggest that we also remove Matplotlib. Why would we want to force it upon users if it is not functional for all Linux and Windows users in the first place?
I would expect pip install mne to have functional interactive plotting out of the box, while I'd expect pip install mne[base] to be "headless".
I would expect
pip install mneto have functional interactive plotting out of the box, while I'd expectpip install mne[base]to be "headless".
unfortunately the project.optional-dependencies cannot remove packages declared under dependencies. So that's not possible.
While the user sees a warning when doing
plt.show(), there is absolutely nothing when doingraw.plot(show=True, block=True). I think this needs to be fixed regardless of making PySide6 a core dependency or not.
This should definitely get fixed. maybe something like this?
from importlib.metadata import metadata
for vizlib in ("PyQt6", "PySide6", ...):
try:
metadata(vizlib)
break
except PackageNotFoundError:
raise ...
Regarding your comments on use cases, to me interactive plotting is something that 95% of all users will want to do almost immediately. I do not have any numbers to back this up, but from my experience and talking to other people, I think this estimate is not far off. Currently, those users who did
pip install mnewill not see a plot, no matter what they do. They have to manuallypip install pyside6. If you are not willing to make PySide6 a core dependency, then there should at least be some warning/error when plotting and no interactive backend is available. But again, this means that most users will have topip install mne pyside6for basic functionality!
I don't think we can really settle this question. AFAICT there are lots of users who only do interactive plotting and/or run scripts on their laptops/desktops-with-screens, and there are also lots of users who run MNE on a remote server or cluster (and probably also do interactive plotting too, but not on that server/cluster). I think it's important to continue to support both use cases (and make things easier for folks who do want the interactive viz capabilities). Some ideas:
- publish a pip-installable
mne-basepackage that excludes Qt stuff (mirrors what we do on conda-forge) - keep
mnefree of Qt stuff, and do a better job of advertising that for interactive use, folks shouldpip install mne[viz]or whatever.
I think these are both great suggestions. Publishing two packages is certainly nice, but could be substantially more effort. Better advertising seems like the easier alternative, but I'm really OK with either solution.
Regarding the absence of any message: I thought that this was exactly the reason why we're still using qtpy. I remember at some point seeing a message along the lines of "No Qt bindings found, please install PySide6, PyQt6, ...". Did this stop working at some point?
Publishing two packages is certainly nice, but could be substantially more effort.
Probably not too bad, mostly a one-time setup cost for CIs.
exactly the reason why we're still using
qtpy
I think the main reason we're using qtpy is that it unifies/abstracts away the differences in the API between PyQt and PySide (the APIs are fairly similar, but there are a dozen or so differences). The warning you refer to was a nice side-effect but not the main motivation.
Did this stop working at some point?
I don't think so? I haven't checked recently but I feel like I've seen it in some venv or other in the last 6 months or so.
@drammock
unfortunately the
project.optional-dependenciescannot remove packages declared underdependencies. So that's not possible.
Maybe we will be able to resolve this once (if) PEP-771 gets accepted … but this might be very distant still
Thinking about it more, the base mne package should probably not have a hard dependency on Pyside6, regardless of how convenient that would be for us. When running in a notebook on mybinder.org, you do not want any Qt at all, but rather ipympl. There are valid use cases for not wanting PySide6 installed even when doing interactive graphics.
I retract my earlier statement about expecting pip install mne to produce working interactive plots out of the box. I am now of the opinion that it is out of scope for MNE-Python to set up a decent interactive python environment for the user upon pip install. It should rather try to fit in to the existing python environment the best it can.
Of course, we can try to be as helpful as we can towards the user. The standalone installer should install PySide6, because it is standalone and does not need to fit in to an existing python install. If the user plots something with show=True (the default) and we can detect that no window is showing, it would be helpful to produce a warning directing the user to information on how to get interactive plotting working, and how to silence this warning.
[Edit: ok, that's what others have already been saying above.]
Our Installation page through pip/conda could use a little work regarding getting interactive plotting, as mne-qt-browser and pyvistaqt are not currently mentioned. We should also end that page with a link to https://mne.tools/stable/install/ides.html as we do with the standalone installer.
For me, it's very simple. Currently, a typical user will not be able to use interactive plots out of the box. Therefore, I'd try to make this easier for the user by adding PySide6 to the core dependencies. I don't know what the problem here is, people running MNE on a headless Linux server surely will not be negatively affected if this package gets installed automatically.