1515from matplotlib .backends .qt_editor ._formsubplottool import UiSubplotTool
1616from .import qt_compat
1717from .qt_compat import (
18- QtCore ,QtGui ,QtWidgets ,__version__ ,QT_API ,
18+ QtCore ,QtGui ,QtWidgets ,Qt , __version__ ,QT_API ,
1919_devicePixelRatioF ,_isdeleted ,_setDevicePixelRatio ,
2020)
2121
2222backend_version = __version__
2323
24+ # Foos = QFlags<Foo> are exported as Qt.Foos on PyQt6 but Qt.Foo on PyQt5, so
25+ # we just hard-code numeric values for simplicity.
26+
2427# SPECIAL_KEYS are keys that do *not* return their unicode name
2528# instead they have manually specified names
26- SPECIAL_KEYS = {QtCore .Qt .Key_Control :'control' ,
27- QtCore .Qt .Key_Shift :'shift' ,
28- QtCore .Qt .Key_Alt :'alt' ,
29- QtCore .Qt .Key_Meta :'super' ,
30- QtCore .Qt .Key_Return :'enter' ,
31- QtCore .Qt .Key_Left :'left' ,
32- QtCore .Qt .Key_Up :'up' ,
33- QtCore .Qt .Key_Right :'right' ,
34- QtCore .Qt .Key_Down :'down' ,
35- QtCore .Qt .Key_Escape :'escape' ,
36- QtCore .Qt .Key_F1 :'f1' ,
37- QtCore .Qt .Key_F2 :'f2' ,
38- QtCore .Qt .Key_F3 :'f3' ,
39- QtCore .Qt .Key_F4 :'f4' ,
40- QtCore .Qt .Key_F5 :'f5' ,
41- QtCore .Qt .Key_F6 :'f6' ,
42- QtCore .Qt .Key_F7 :'f7' ,
43- QtCore .Qt .Key_F8 :'f8' ,
44- QtCore .Qt .Key_F9 :'f9' ,
45- QtCore .Qt .Key_F10 :'f10' ,
46- QtCore .Qt .Key_F11 :'f11' ,
47- QtCore .Qt .Key_F12 :'f12' ,
48- QtCore .Qt .Key_Home :'home' ,
49- QtCore .Qt .Key_End :'end' ,
50- QtCore .Qt .Key_PageUp :'pageup' ,
51- QtCore .Qt .Key_PageDown :'pagedown' ,
52- QtCore .Qt .Key_Tab :'tab' ,
53- QtCore .Qt .Key_Backspace :'backspace' ,
54- QtCore .Qt .Key_Enter :'enter' ,
55- QtCore .Qt .Key_Insert :'insert' ,
56- QtCore .Qt .Key_Delete :'delete' ,
57- QtCore .Qt .Key_Pause :'pause' ,
58- QtCore .Qt .Key_SysReq :'sysreq' ,
59- QtCore .Qt .Key_Clear :'clear' , }
29+ SPECIAL_KEYS = {
30+ Qt .Key .Key_Control :'control' ,
31+ Qt .Key .Key_Shift :'shift' ,
32+ Qt .Key .Key_Alt :'alt' ,
33+ Qt .Key .Key_Meta :'super' ,
34+ Qt .Key .Key_Return :'enter' ,
35+ Qt .Key .Key_Left :'left' ,
36+ Qt .Key .Key_Up :'up' ,
37+ Qt .Key .Key_Right :'right' ,
38+ Qt .Key .Key_Down :'down' ,
39+ Qt .Key .Key_Escape :'escape' ,
40+ Qt .Key .Key_F1 :'f1' ,
41+ Qt .Key .Key_F2 :'f2' ,
42+ Qt .Key .Key_F3 :'f3' ,
43+ Qt .Key .Key_F4 :'f4' ,
44+ Qt .Key .Key_F5 :'f5' ,
45+ Qt .Key .Key_F6 :'f6' ,
46+ Qt .Key .Key_F7 :'f7' ,
47+ Qt .Key .Key_F8 :'f8' ,
48+ Qt .Key .Key_F9 :'f9' ,
49+ Qt .Key .Key_F10 :'f10' ,
50+ Qt .Key .Key_F11 :'f11' ,
51+ Qt .Key .Key_F12 :'f12' ,
52+ Qt .Key .Key_Home :'home' ,
53+ Qt .Key .Key_End :'end' ,
54+ Qt .Key .Key_PageUp :'pageup' ,
55+ Qt .Key .Key_PageDown :'pagedown' ,
56+ Qt .Key .Key_Tab :'tab' ,
57+ Qt .Key .Key_Backspace :'backspace' ,
58+ Qt .Key .Key_Enter :'enter' ,
59+ Qt .Key .Key_Insert :'insert' ,
60+ Qt .Key .Key_Delete :'delete' ,
61+ Qt .Key .Key_Pause :'pause' ,
62+ Qt .Key .Key_SysReq :'sysreq' ,
63+ Qt .Key .Key_Clear :'clear' ,
64+ }
6065if sys .platform == 'darwin' :
6166# in OSX, the control and super (aka cmd/apple) keys are switched, so
6267# switch them back.
63- SPECIAL_KEYS .update ({QtCore .Qt .Key_Control :'cmd' ,# cmd/apple key
64- QtCore .Qt .Key_Meta :'control' ,
65- })
68+ SPECIAL_KEYS .update ({
69+ Qt .Key .Key_Control :'cmd' ,# cmd/apple key
70+ Qt .Key .Key_Meta :'control' ,
71+ })
6672# Define which modifier keys are collected on keyboard events.
67- # Elements are (Modifier Flag , Qt Key) tuples.
73+ # Elements are (Qt::KeyboardModifier(s) , Qt Key) tuples.
6874# Order determines the modifier order (ctrl+alt+...) reported by Matplotlib.
6975_MODIFIER_KEYS = [
70- (QtCore . Qt . ShiftModifier , QtCore . Qt .Key_Shift ),
71- (QtCore . Qt . ControlModifier , QtCore . Qt .Key_Control ),
72- (QtCore . Qt . AltModifier , QtCore . Qt .Key_Alt ),
73- (QtCore . Qt . MetaModifier , QtCore . Qt .Key_Meta ),
76+ (0x02000000 , Qt . Key .Key_Shift ),
77+ (0x04000000 , Qt . Key .Key_Control ),
78+ (0x08000000 , Qt . Key .Key_Alt ),
79+ (0x10000000 , Qt . Key .Key_Meta ),
7480]
7581cursord = {
76- cursors .MOVE :QtCore . Qt .SizeAllCursor ,
77- cursors .HAND :QtCore . Qt .PointingHandCursor ,
78- cursors .POINTER :QtCore . Qt .ArrowCursor ,
79- cursors .SELECT_REGION :QtCore . Qt .CrossCursor ,
80- cursors .WAIT :QtCore . Qt .WaitCursor ,
81- }
82+ cursors .MOVE :Qt . CursorShape .SizeAllCursor ,
83+ cursors .HAND :Qt . CursorShape .PointingHandCursor ,
84+ cursors .POINTER :Qt . CursorShape .ArrowCursor ,
85+ cursors .SELECT_REGION :Qt . CursorShape .CrossCursor ,
86+ cursors .WAIT :Qt . CursorShape .WaitCursor ,
87+ }
8288SUPER = 0 # Deprecated.
8389ALT = 1 # Deprecated.
8490CTRL = 2 # Deprecated.
8793 (SPECIAL_KEYS [key ],mod ,key )for mod ,key in _MODIFIER_KEYS ]
8894
8995
96+ def _to_int (x ):
97+ return x .value if QT_API == "PyQt6" else int (x )
98+
99+
90100# make place holder
91101qApp = None
92102
@@ -140,17 +150,17 @@ def _allow_super_init(__init__):
140150 Decorator for ``__init__`` to allow ``super().__init__`` on PyQt4/PySide2.
141151 """
142152
143- if QT_API == "PyQt5" :
153+ if QT_API in [ "PyQt5" , "PyQt6" ] :
144154
145155return __init__
146156
147157else :
148- # To work around lack of cooperative inheritance in PyQt4, PySide,
149- #and PySide2 , when calling FigureCanvasQT.__init__, we temporarily
158+ # To work around lack of cooperative inheritance in PyQt4 and
159+ #PySide{,2,6} , when calling FigureCanvasQT.__init__, we temporarily
150160# patch QWidget.__init__ by a cooperative version, that first calls
151161# QWidget.__init__ with no additional arguments, and then finds the
152162# next class in the MRO with an __init__ that does support cooperative
153- # inheritance (i.e., not defined by the PyQt4, PySide, PySide2, sip
163+ # inheritance (i.e., not defined by the PyQt4 or sip, or PySide{,2,6}
154164# or Shiboken packages), and manually call its `__init__`, once again
155165# passing the additional arguments.
156166
@@ -162,7 +172,9 @@ def cooperative_qwidget_init(self, *args, **kwargs):
162172next_coop_init = next (
163173cls for cls in mro [mro .index (QtWidgets .QWidget )+ 1 :]
164174if cls .__module__ .split ("." )[0 ]not in [
165- "PyQt4" ,"sip" ,"PySide" ,"PySide2" ,"Shiboken" ])
175+ "PyQt4" ,"sip" ,
176+ "PySide" ,"PySide2" ,"PySide6" ,"Shiboken" ,
177+ ])
166178next_coop_init .__init__ (self ,* args ,** kwargs )
167179
168180@functools .wraps (__init__ )
@@ -207,13 +219,13 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
207219required_interactive_framework = "qt5"
208220_timer_cls = TimerQT
209221
210- # map Qt button codes to MouseEvent's ones:
211- buttond = { QtCore . Qt . LeftButton :MouseButton .LEFT ,
212- QtCore . Qt . MidButton :MouseButton .MIDDLE ,
213- QtCore . Qt . RightButton :MouseButton .RIGHT ,
214- QtCore . Qt . XButton1 :MouseButton .BACK ,
215- QtCore . Qt . XButton2 :MouseButton .FORWARD ,
216- }
222+ buttond = { # Map Qt::MouseButton(s) to MouseEvents.
223+ 0x01 :MouseButton .LEFT ,
224+ 0x02 :MouseButton .RIGHT ,
225+ 0x04 :MouseButton .MIDDLE ,
226+ 0x08 :MouseButton .BACK ,
227+ 0x10 :MouseButton .FORWARD ,
228+ }
217229
218230@_allow_super_init
219231def __init__ (self ,figure ):
@@ -233,11 +245,11 @@ def __init__(self, figure):
233245self ._is_drawing = False
234246self ._draw_rect_callback = lambda painter :None
235247
236- self .setAttribute (QtCore . Qt .WA_OpaquePaintEvent )
248+ self .setAttribute (Qt . WidgetAttribute .WA_OpaquePaintEvent )
237249self .setMouseTracking (True )
238250self .resize (* self .get_width_height ())
239251
240- palette = QtGui .QPalette (QtCore . Qt . white )
252+ palette = QtGui .QPalette (QtGui . QColor ( " white" ) )
241253self .setPalette (palette )
242254
243255def _update_figure_dpi (self ):
@@ -311,14 +323,14 @@ def mouseEventCoords(self, pos):
311323
312324def mousePressEvent (self ,event ):
313325x ,y = self .mouseEventCoords (event .pos ())
314- button = self .buttond .get (event .button ())
326+ button = self .buttond .get (_to_int ( event .button () ))
315327if button is not None :
316328FigureCanvasBase .button_press_event (self ,x ,y ,button ,
317329guiEvent = event )
318330
319331def mouseDoubleClickEvent (self ,event ):
320332x ,y = self .mouseEventCoords (event .pos ())
321- button = self .buttond .get (event .button ())
333+ button = self .buttond .get (_to_int ( event .button () ))
322334if button is not None :
323335FigureCanvasBase .button_press_event (self ,x ,y ,
324336button ,dblclick = True ,
@@ -330,7 +342,7 @@ def mouseMoveEvent(self, event):
330342
331343def mouseReleaseEvent (self ,event ):
332344x ,y = self .mouseEventCoords (event )
333- button = self .buttond .get (event .button ())
345+ button = self .buttond .get (_to_int ( event .button () ))
334346if button is not None :
335347FigureCanvasBase .button_release_event (self ,x ,y ,button ,
336348guiEvent = event )
@@ -368,6 +380,9 @@ def keyReleaseEvent(self, event):
368380FigureCanvasBase .key_release_event (self ,key ,guiEvent = event )
369381
370382def resizeEvent (self ,event ):
383+ frame = sys ._getframe ()
384+ if frame .f_code is frame .f_back .f_code :# Prevent PyQt6 recursion.
385+ return
371386w = event .size ().width ()* self ._dpi_ratio
372387h = event .size ().height ()* self ._dpi_ratio
373388dpival = self .figure .dpi
@@ -388,7 +403,7 @@ def minumumSizeHint(self):
388403
389404def _get_key (self ,event ):
390405event_key = event .key ()
391- event_mods = int (event .modifiers ())# actually a bitmask
406+ event_mods = _to_int (event .modifiers ())# actually a bitmask
392407
393408# get names of the pressed modifier keys
394409# 'control' is named 'control' when a standalone key, but 'ctrl' when a
@@ -433,7 +448,7 @@ def start_event_loop(self, timeout=0):
433448if timeout > 0 :
434449timer = QtCore .QTimer .singleShot (int (timeout * 1000 ),
435450event_loop .quit )
436- event_loop . exec_ ( )
451+ qt_compat . _exec ( event_loop )
437452
438453def stop_event_loop (self ,event = None ):
439454# docstring inherited
@@ -575,7 +590,7 @@ def __init__(self, canvas, num):
575590# StrongFocus accepts both tab and click to focus and will enable the
576591# canvas to process event without clicking.
577592# https://doc.qt.io/qt-5/qt.html#FocusPolicy-enum
578- self .canvas .setFocusPolicy (QtCore . Qt .StrongFocus )
593+ self .canvas .setFocusPolicy (Qt . FocusPolicy .StrongFocus )
579594self .canvas .setFocus ()
580595
581596self .window .raise_ ()
@@ -654,8 +669,8 @@ class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar):
654669def __init__ (self ,canvas ,parent ,coordinates = True ):
655670"""coordinates: should we show the coordinates on the right?"""
656671QtWidgets .QToolBar .__init__ (self ,parent )
657- self .setAllowedAreas (
658- QtCore . Qt .TopToolBarArea | QtCore . Qt . BottomToolBarArea )
672+ self .setAllowedAreas (# Qt::TopToolBarArea | BottomToolBarArea
673+ Qt .ToolBarAreas ( 0x4 | 0x8 ) )
659674
660675self .coordinates = coordinates
661676self ._actions = {}# mapping of toolitem method names to QActions.
@@ -677,11 +692,11 @@ def __init__(self, canvas, parent, coordinates=True):
677692# will resize this label instead of the buttons.
678693if self .coordinates :
679694self .locLabel = QtWidgets .QLabel ("" ,self )
680- self .locLabel .setAlignment (
681- QtCore . Qt .AlignRight | QtCore . Qt . AlignVCenter )
695+ self .locLabel .setAlignment (# Qt::AlignRight | AlignVCenter
696+ Qt .Alignment ( 0x02 | 0x80 ) )
682697self .locLabel .setSizePolicy (
683- QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Expanding ,
684- QtWidgets .QSizePolicy .Ignored ))
698+ QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Policy . Expanding ,
699+ QtWidgets .QSizePolicy .Policy . Ignored ))
685700labelAction = self .addWidget (self .locLabel )
686701labelAction .setVisible (True )
687702
@@ -715,7 +730,7 @@ def _icon(self, name):
715730if self .palette ().color (self .backgroundRole ()).value ()< 128 :
716731icon_color = self .palette ().color (self .foregroundRole ())
717732mask = pm .createMaskFromColor (QtGui .QColor ('black' ),
718- QtCore . Qt .MaskOutColor )
733+ Qt . MaskMode .MaskOutColor )
719734pm .fill (icon_color )
720735pm .setMask (mask )
721736return QtGui .QIcon (pm )
@@ -785,7 +800,7 @@ def configure_subplots(self):
785800image = str (cbook ._get_data_path ('images/matplotlib.png' ))
786801dia = SubplotToolQt (self .canvas .figure ,self .canvas .parent ())
787802dia .setWindowIcon (QtGui .QIcon (image ))
788- dia . exec_ ( )
803+ qt_compat . _exec ( dia )
789804
790805def save_figure (self ,* args ):
791806filetypes = self .canvas .get_supported_filetypes_grouped ()
@@ -874,7 +889,7 @@ def _export_values(self):
874889QtGui .QFontMetrics (text .document ().defaultFont ())
875890 .size (0 ,text .toPlainText ()).height ()+ 20 )
876891text .setMaximumSize (size )
877- dialog . exec_ ( )
892+ qt_compat . _exec ( dialog )
878893
879894def _on_value_changed (self ):
880895self ._figure .subplots_adjust (** {attr :self ._widgets [attr ].value ()
@@ -899,14 +914,14 @@ class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar):
899914def __init__ (self ,toolmanager ,parent ):
900915ToolContainerBase .__init__ (self ,toolmanager )
901916QtWidgets .QToolBar .__init__ (self ,parent )
902- self .setAllowedAreas (
903- QtCore . Qt .TopToolBarArea | QtCore . Qt . BottomToolBarArea )
917+ self .setAllowedAreas (# Qt::TopToolBarArea | BottomToolBarArea
918+ Qt .ToolBarAreas ( 0x4 | 0x8 ) )
904919message_label = QtWidgets .QLabel ("" )
905- message_label .setAlignment (
906- QtCore . Qt .AlignRight | QtCore . Qt . AlignVCenter )
920+ message_label .setAlignment (# Qt::AlignRight | AlignVCenter
921+ Qt .Alignment ( 0x02 | 0x80 ) )
907922message_label .setSizePolicy (
908- QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Expanding ,
909- QtWidgets .QSizePolicy .Ignored ))
923+ QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Policy . Expanding ,
924+ QtWidgets .QSizePolicy .Policy . Ignored ))
910925self ._message_action = self .addWidget (message_label )
911926self ._toolitems = {}
912927self ._groups = {}
@@ -1031,7 +1046,7 @@ def mainloop():
10311046if is_python_signal_handler :
10321047signal .signal (signal .SIGINT ,signal .SIG_DFL )
10331048try :
1034- qApp . exec_ ( )
1049+ qt_compat . _exec ( qApp )
10351050finally :
10361051# reset the SIGINT exception handler
10371052if is_python_signal_handler :