ipykernel
ipykernel copied to clipboard
support for %gui qt6
Ipython already provides support for qt6. for example running this in ipython works:
%gui qt6
however when running the same using the ipykernel i get
%gui qt6
ERROR:root:Invalid GUI request 'qt6', valid ones are:dict_keys(['inline', 'nbagg', 'notebook', 'ipympl', 'widget', None, 'qt4', 'qt', 'qt5', 'wx', 'tk', 'gtk', 'gtk3', 'osx', 'asyncio'])
Hi @daliagachc, if you'd like to try a fix and verify it in your environment, it should be a matter of adding a loop_qt6 function and exit handler similar to what is done for qt5 here.
+1, there was https://github.com/ipython/ipython/pull/13085 on the IPython side.
If Qt6 is installed it looks like matplotlib might be automatically trying to set qt6 up. We have test errors on napari with install --pre and matplotlib:
/tmp/.tox/py38-linux-pyside/lib/python3.8/site-packages/matplotlib/pyplot.py:2467: in <module>
install_repl_displayhook()
/tmp/.tox/py38-linux-pyside/lib/python3.8/site-packages/matplotlib/pyplot.py:138: in install_repl_displayhook
ip.enable_gui(ipython_gui_name)
/tmp/.tox/py38-linux-pyside/lib/python3.8/site-packages/ipykernel/inprocess/ipkernel.py:181: in enable_gui
enable_gui(gui, kernel=self.kernel)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
gui = 'qt6'
kernel = <ipykernel.inprocess.ipkernel.InProcessKernel object at 0x7fcb20138070>
def enable_gui(gui, kernel=None):
"""Enable integration with a given GUI"""
if gui not in loop_map:
e = "Invalid GUI request %r, valid ones are:%s" % (gui, loop_map.keys())
> raise ValueError(e)
E ValueError: Invalid GUI request 'qt6', valid ones are:dict_keys(['inline', 'nbagg', 'notebook', 'ipympl', 'widget', None, 'qt4', 'qt', 'qt5', 'wx', 'tk', 'gtk', 'gtk3', 'osx', 'asyncio'])
@tacaswell FYI.
You need to implement an event loop for this in ipykernel/eventloops.py. If Qt6 didn't change too much with respect to version 5, it should be pretty straightforward.
hi! thanks for the input. so i did implement qt6 in a similar fashion to qt5. now gui qt6 runs from jlab for example without interrupting the kernel. however when i execute any cell in jlab the qt windows disappears
Sent from my iPhone
On 23. Sep 2021, at 1.56, Carlos Cordoba @.***> wrote:
You need to implement an event loop for this in ipykernel/eventloops.py. If Qt6 didn't change too much with respect to version 5, it should be pretty straightforward.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.
This appears to result from a weird symbiosis of Matplotlib and IPython. When you try to import pyplot within an ipykernel, Matplotlib queries IPython to get a valid Qt backend. The following code is in pyplot.py (lines 147-151):
from IPython.core.pylabtools import backend2gui
# trigger IPython's eventloop integration, if available
ipython_gui_name = backend2gui.get(get_backend())
if ipython_gui_name:
ip.enable_gui(ipython_gui_name)
If the Matplotlib use function has not been called to initialize a backend, it defaults to QtAgg, but the backend2gui dictionary in IPython 7.28 now maps that to qt6, which ipykernel does not recognize as a valid ipython_gui_name.
I can see that adding a loop_qt6 handler for PyQt6 would fix the name issue, but does that mean that it will just attempt to use PyQt6 even when it's not installed? If so, it looks as if switching QtAgg to default to PyQt6 may have been premature since it's not yet available on conda or supported by the qtpy package.
but the backend2gui dictionary in IPython 7.28 now maps that to qt6, which ipykernel does not recognize as a valid ipython_gui_name
I also think that's not the right approach, at least until Qt6 has more widespread usage.
@Carreau, would you accept a patch to revert it?
@Carreau, would you accept a patch to revert it?
Yes, that would be ok.
I will try to look at this tomorrow.
There is another more worrying aspect of the Matplotlib-IPython symbiosis. This is almost certainly a separate issue with the InProcessKernel, but it might also reflect issues with Matplotlib and IPython as well, which the people reading this might help me to identify. When you import 'pyplot', it always runs the install_repl_displayhook function that triggers the call to enable_gui that I referred to above. This then sets the kernel.eventloop to one of the loop functions defined in 'eventloops.py'. I think this is clearly a bug in the InProcessKernel, because it triggers calls to a second event loop, which the kernel is not set up for (e.g., kernel.io_loop is not defined). However, I worry about other side effects. Should a simple import do something as profound as changing event loops? Shouldn't that be the job of a specific function call like mpl.use()?
that triggers the call to enable_gui that I referred to above.
What if you simply add a return to that call if the kernel is an instance of InProcessKernel? The problem is that kernel is not very well supported because most people here focus on the two processes kernel (i.e. IPythonKernel) because it's the one that allows users to run cpu-intensive code out of our applications.
Should a simple import do something as profound as changing event loops?
Perhaps not, but I think that's the case to allow interactive plotting work out of the box as soon as you import pyplot. A lot of people is unaware of event loops and how to activate them.
@ccordoba12, I agree that is probably the reason pyplot does what it does. I think I know what to put in a PR to fix #780, which will probably resolve all these problems for me. I know that the InProcessKernel is often on life support, but it is essential to my application, which is why I monitor these discussions when I have the time.
I think I know what to put in a PR to fix #780, which will probably resolve all these problems for me
Please push it and I'll be happy to review it.
Ah, I did not understand that IPython and ipykernel each had their own event loop code.
pyplot should be thought of as the "application" layer of Matplotlib that "just works" out of the box. We picked up the mpl -> ipython calls a while ago to remove the need for %matplotlib in the IPython shell. On the other hand mpl.use mostly just sets an rcparams and has no other impact if you have not already imported pyplot (if pyplot is imported we try to force it to change backends which may fail).
We have gone through some effort on the mpl side to avoid locking you into a given GUI framework until you create the first figure, however it seems that the install_repl_displayhook has more side effects that my 2015 comment just above that function call suggest! I would be :+1: on deferring the initial call to that function to the first time a figure gets made (I think I see how to do it....)
If the Matplotlib use function has not been called to initialize a backend, it defaults to QtAgg, but the backend2gui dictionary in IPython 7.28 now maps that to qt6, which ipykernel does not recognize as a valid ipython_gui_name.
ah, the issue here is that on the Matplotlib side Qt5 and Qt6 are similar enough that we have stuffed all of the compatibility logic back into qt_compat.py and unlike qt4 -> qt5 when we had distinct backends, we now only have QtAgg (Qt5Agg has been turned into a re-export shim for back-compatibility). In pt_inputhook/qt.py already did the same thing for Qt4 and Qt5, in ipython/ipython#13085 I did the same thing for Qt6 as well.
However, the bug I introduced was that I set up the mapping backends = { ..., 'qt6': 'QtAgg', ...} so that %matplotlib qt6 does the right (ish) thing (it will actually silently fallback to qt5....maybe that is a bug...), but when backend2gui is set up it goes back to a non-existent loop. The fix for that is to fix the reverse mapping "by hand" (like a majority of the entries at this point!).
With https://github.com/ipython/ipython/pull/13179 I think the napari failures will be fixed, looking at the ipykernel event loop to see what needs to be ported from IPython...
I have a patch that get qt6 working (and solves the quickly closing windows from @daliagachc ), but there are some issues with selecting the right version of Qt which may be a bug in any of Matplotlib, IPython, or ipykernel 😞 .
hi! thanks for the input. so i did implement qt6 in a similar fashion to qt5. now gui qt6 runs from jlab for example without interrupting the kernel. however when i execute any cell in jlab the qt windows disappears
The window disappears because [even today] the way ipykernel ties in the event loop is different than IPython. In IPython, an explicit instance of QEventLoop is obtained and executed. "Quitting" it effectively pauses the event loop.
In ipykernel, it executes/quits a QApplication instance instead, and quitting that will destroy all Qt Windows.
It seems #782 is quite a ways toward the solution (including addressing this window disappearance); I was working on this today and adding support for PySide6. @tacaswell what's the status of that PR?
Dormant. I had to re-read this and remind my self that ipykernel and ipython have their own event loop integrations .... 🤦🏻
@shaperilio Please feel free to take over that PR (or drop my commit and start from scratch). I would recommend using IPython as a reference implementation either way.
Will do; thanks @tacaswell - you definitely made some changes I'll adopt.