diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py index 7dfa84c..0699994 100644 --- a/IPython/core/magics/basic.py +++ b/IPython/core/magics/basic.py @@ -493,8 +493,10 @@ Currently the magic system has the following functions:""", are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX):: %gui wx # enable wxPython event loop integration - %gui qt4|qt # enable PyQt4 event loop integration - %gui qt5 # enable PyQt5 event loop integration + %gui qt # enable PyQt/PySide event loop integration + # with the latest version available. + %gui qt6 # enable PyQt6/PySide6 event loop integration + %gui qt5 # enable PyQt5/PySide2 event loop integration %gui gtk # enable PyGTK event loop integration %gui gtk3 # enable Gtk3 event loop integration %gui gtk4 # enable Gtk4 event loop integration diff --git a/IPython/external/qt_for_kernel.py b/IPython/external/qt_for_kernel.py index 986cae3..11e8862 100644 --- a/IPython/external/qt_for_kernel.py +++ b/IPython/external/qt_for_kernel.py @@ -45,7 +45,6 @@ from IPython.external.qt_loaders import ( QT_API_PYQT5, QT_API_PYSIDE2, # QT4 - QT_API_PYQTv1, QT_API_PYQT, QT_API_PYSIDE, # default @@ -59,10 +58,6 @@ _qt_apis = ( # QT5 QT_API_PYQT5, QT_API_PYSIDE2, - # QT4 - QT_API_PYQTv1, - QT_API_PYQT, - QT_API_PYSIDE, # default QT_API_PYQT_DEFAULT, ) @@ -116,8 +111,6 @@ def get_options(): QT_API_PYSIDE6, QT_API_PYQT5, QT_API_PYSIDE2, - QT_API_PYQT, - QT_API_PYSIDE, ] elif qt_api not in _qt_apis: raise RuntimeError("Invalid Qt API %r, valid values are: %r" % diff --git a/IPython/external/qt_loaders.py b/IPython/external/qt_loaders.py index f3831da..c900c8f 100644 --- a/IPython/external/qt_loaders.py +++ b/IPython/external/qt_loaders.py @@ -24,6 +24,7 @@ QT_API_PYQT5 = 'pyqt5' QT_API_PYSIDE2 = 'pyside2' # Qt4 +# NOTE: Here for legacy matplotlib compatibility, but not really supported on the IPython side. QT_API_PYQT = "pyqt" # Force version 2 QT_API_PYQTv1 = "pyqtv1" # Force version 2 QT_API_PYSIDE = "pyside" @@ -374,20 +375,16 @@ def load_qt(api_options): PySide6 is available, and only one is imported per session. Currently-imported Qt library: %r - PyQt4 available (requires QtCore, QtGui, QtSvg): %s PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s PyQt6 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s - PySide >= 1.0.3 installed: %s PySide2 installed: %s PySide6 installed: %s Tried to load: %r """ % ( loaded_api(), - has_binding(QT_API_PYQT), has_binding(QT_API_PYQT5), has_binding(QT_API_PYQT6), - has_binding(QT_API_PYSIDE), has_binding(QT_API_PYSIDE2), has_binding(QT_API_PYSIDE6), api_options, diff --git a/IPython/lib/guisupport.py b/IPython/lib/guisupport.py index cfd325e..9089fa8 100644 --- a/IPython/lib/guisupport.py +++ b/IPython/lib/guisupport.py @@ -106,11 +106,11 @@ def start_event_loop_wx(app=None): app._in_event_loop = True #----------------------------------------------------------------------------- -# qt4 +# Qt #----------------------------------------------------------------------------- -def get_app_qt4(*args, **kwargs): - """Create a new qt4 app or return an existing one.""" +def get_app_qt(*args, **kwargs): + """Create a new Qt app or return an existing one.""" from IPython.external.qt_for_kernel import QtGui app = QtGui.QApplication.instance() if app is None: @@ -119,8 +119,8 @@ def get_app_qt4(*args, **kwargs): app = QtGui.QApplication(*args, **kwargs) return app -def is_event_loop_running_qt4(app=None): - """Is the qt4 event loop running.""" +def is_event_loop_running_qt(app=None): + """Is the qt event loop running.""" # New way: check attribute on shell instance ip = get_ipython() if ip is not None: @@ -128,18 +128,18 @@ def is_event_loop_running_qt4(app=None): # Old way: check attribute on QApplication singleton if app is None: - app = get_app_qt4(['']) + app = get_app_qt(['']) if hasattr(app, '_in_event_loop'): return app._in_event_loop else: - # Does qt4 provide a other way to detect this? + # Does qt provide a other way to detect this? return False -def start_event_loop_qt4(app=None): - """Start the qt4 event loop in a consistent manner.""" +def start_event_loop_qt(app=None): + """Start the qt event loop in a consistent manner.""" if app is None: - app = get_app_qt4(['']) - if not is_event_loop_running_qt4(app): + app = get_app_qt(['']) + if not is_event_loop_running_qt(app): app._in_event_loop = True app.exec_() app._in_event_loop = False diff --git a/IPython/terminal/pt_inputhooks/__init__.py b/IPython/terminal/pt_inputhooks/__init__.py index 146424c..0e6bacc 100644 --- a/IPython/terminal/pt_inputhooks/__init__.py +++ b/IPython/terminal/pt_inputhooks/__init__.py @@ -8,7 +8,6 @@ aliases = { backends = [ "qt", - "qt4", "qt5", "qt6", "gtk", @@ -80,21 +79,7 @@ def set_qt_api(gui): f'environment variable is set to "{qt_api}"' ) else: - # NOTE: 'qt4' is not selectable because it's set as an alias for 'qt'; see `aliases` above. - if gui == "qt4": - try: - import PyQt # noqa - - os.environ["QT_API"] = "pyqt" - except ImportError: - try: - import PySide # noqa - - os.environ["QT_API"] = "pyside" - except ImportError: - # Neither implementation installed; set it to something so IPython gives an error - os.environ["QT_API"] = "pyqt" - elif gui == "qt5": + if gui == "qt5": try: import PyQt5 # noqa @@ -124,7 +109,7 @@ def set_qt_api(gui): del os.environ["QT_API"] else: raise ValueError( - f'Unrecognized Qt version: {gui}. Should be "qt4", "qt5", "qt6", or "qt".' + f'Unrecognized Qt version: {gui}. Should be "qt5", "qt6", or "qt".' ) diff --git a/IPython/terminal/tests/test_pt_inputhooks.py b/IPython/terminal/tests/test_pt_inputhooks.py index 775d1c7..bb4baaa 100644 --- a/IPython/terminal/tests/test_pt_inputhooks.py +++ b/IPython/terminal/tests/test_pt_inputhooks.py @@ -12,7 +12,7 @@ guis_avail = [] def _get_qt_vers(): """If any version of Qt is available, this will populate `guis_avail` with 'qt' and 'qtx'. Due to the import mechanism, we can't import multiple versions of Qt in one session.""" - for gui in ["qt", "qt6", "qt5", "qt4"]: + for gui in ["qt", "qt6", "qt5"]: print(f"Trying {gui}") try: set_qt_api(gui) @@ -39,7 +39,7 @@ def test_inputhook_qt(): get_inputhook_name_and_func(gui) # ...and now we're stuck with this version of Qt for good; can't switch. - for not_gui in ["qt6", "qt5", "qt4"]: + for not_gui in ["qt6", "qt5"]: if not_gui not in guis_avail: break diff --git a/docs/source/config/eventloops.rst b/docs/source/config/eventloops.rst index dd527a6..6bf349f 100644 --- a/docs/source/config/eventloops.rst +++ b/docs/source/config/eventloops.rst @@ -7,7 +7,7 @@ loop, so you can use both a GUI and an interactive prompt together. IPython supports a number of common GUI toolkits, but from IPython 3.0, it is possible to integrate other event loops without modifying IPython itself. -Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, ``gtk4``, +Supported event loops include ``qt5``, ``qt6``, ``gtk2``, ``gtk3``, ``gtk4``, ``wx``, ``osx`` and ``tk``. Make sure the event loop you specify matches the GUI toolkit used by your own code. diff --git a/docs/source/interactive/reference.rst b/docs/source/interactive/reference.rst index 8eed534..1df77dc 100644 --- a/docs/source/interactive/reference.rst +++ b/docs/source/interactive/reference.rst @@ -44,7 +44,7 @@ the command-line by passing the full class name and a corresponding value; type <...snip...> --matplotlib= (InteractiveShellApp.matplotlib) Default: None - Choices: ['auto', 'gtk', 'gtk3', 'gtk4', 'inline', 'nbagg', 'notebook', 'osx', 'qt', 'qt4', 'qt5', 'tk', 'wx'] + Choices: ['auto', 'gtk', 'gtk3', 'gtk4', 'inline', 'nbagg', 'notebook', 'osx', 'qt', 'qt5', 'qt6', 'tk', 'wx'] Configure matplotlib for interactive use with the default matplotlib backend. <...snip...> @@ -892,7 +892,7 @@ GUI event loop support ====================== IPython has excellent support for working interactively with Graphical User -Interface (GUI) toolkits, such as wxPython, PyQt4/PySide, PyGTK and Tk. This is +Interface (GUI) toolkits, such as wxPython, PyQt/PySide, PyGTK and Tk. This is implemented by running the toolkit's event loop while IPython is waiting for input. @@ -902,7 +902,7 @@ For users, enabling GUI event loop integration is simple. You simple use the %gui [GUINAME] With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME`` -arguments include ``wx``, ``qt``, ``qt5``, ``gtk``, ``gtk3`` ``gtk4``, and +arguments include ``wx``, ``qt``, ``qt5``, ``qt6``, ``gtk``, ``gtk3`` ``gtk4``, and ``tk``. Thus, to use wxPython interactively and create a running :class:`wx.App` @@ -936,16 +936,9 @@ PyQt and PySide .. attempt at explanation of the complete mess that is Qt support When you use ``--gui=qt`` or ``--matplotlib=qt``, IPython can work with either -PyQt4 or PySide. There are three options for configuration here, because -PyQt4 has two APIs for QString and QVariant: v1, which is the default on -Python 2, and the more natural v2, which is the only API supported by PySide. -v2 is also the default for PyQt4 on Python 3. IPython's code for the QtConsole -uses v2, but you can still use any interface in your code, since the -Qt frontend is in a different process. - -The default will be to import PyQt4 without configuration of the APIs, thus -matching what most applications would expect. It will fall back to PySide if -PyQt4 is unavailable. +PyQt or PySide. ``qt`` implies "use the latest version available", and it favors +PyQt over PySide. To request a specific version, use ``qt5`` or ``qt6``. Note that +Qt4 is not supported with the ``--gui`` switch (and has not been for some time now). If specified, IPython will respect the environment variable ``QT_API`` used by ETS. ETS 4.0 also works with both PyQt4 and PySide, but it requires