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
145155 return __init__
146156
147157 else :
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):
162172 next_coop_init = next (
163173 cls for cls in mro [mro .index (QtWidgets .QWidget ) + 1 :]
164174 if cls .__module__ .split ("." )[0 ] not in [
165- "PyQt4" , "sip" , "PySide" , "PySide2" , "Shiboken" ])
175+ "PyQt4" , "sip" ,
176+ "PySide" , "PySide2" , "PySide6" , "Shiboken" ,
177+ ])
166178 next_coop_init .__init__ (self , * args , ** kwargs )
167179
168180 @functools .wraps (__init__ )
@@ -207,13 +219,13 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
207219 required_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
219231 def __init__ (self , figure ):
@@ -233,11 +245,11 @@ def __init__(self, figure):
233245 self ._is_drawing = False
234246 self ._draw_rect_callback = lambda painter : None
235247
236- self .setAttribute (QtCore . Qt .WA_OpaquePaintEvent )
248+ self .setAttribute (Qt . WidgetAttribute .WA_OpaquePaintEvent )
237249 self .setMouseTracking (True )
238250 self .resize (* self .get_width_height ())
239251
240- palette = QtGui .QPalette (QtCore . Qt . white )
252+ palette = QtGui .QPalette (QtGui . QColor ( " white" ) )
241253 self .setPalette (palette )
242254
243255 def _update_figure_dpi (self ):
@@ -311,14 +323,14 @@ def mouseEventCoords(self, pos):
311323
312324 def mousePressEvent (self , event ):
313325 x , y = self .mouseEventCoords (event .pos ())
314- button = self .buttond .get (event .button ())
326+ button = self .buttond .get (_to_int ( event .button () ))
315327 if button is not None :
316328 FigureCanvasBase .button_press_event (self , x , y , button ,
317329 guiEvent = event )
318330
319331 def mouseDoubleClickEvent (self , event ):
320332 x , y = self .mouseEventCoords (event .pos ())
321- button = self .buttond .get (event .button ())
333+ button = self .buttond .get (_to_int ( event .button () ))
322334 if button is not None :
323335 FigureCanvasBase .button_press_event (self , x , y ,
324336 button , dblclick = True ,
@@ -330,7 +342,7 @@ def mouseMoveEvent(self, event):
330342
331343 def mouseReleaseEvent (self , event ):
332344 x , y = self .mouseEventCoords (event )
333- button = self .buttond .get (event .button ())
345+ button = self .buttond .get (_to_int ( event .button () ))
334346 if button is not None :
335347 FigureCanvasBase .button_release_event (self , x , y , button ,
336348 guiEvent = event )
@@ -368,6 +380,9 @@ def keyReleaseEvent(self, event):
368380 FigureCanvasBase .key_release_event (self , key , guiEvent = event )
369381
370382 def resizeEvent (self , event ):
383+ frame = sys ._getframe ()
384+ if frame .f_code is frame .f_back .f_code : # Prevent PyQt6 recursion.
385+ return
371386 w = event .size ().width () * self ._dpi_ratio
372387 h = event .size ().height () * self ._dpi_ratio
373388 dpival = self .figure .dpi
@@ -388,7 +403,7 @@ def minumumSizeHint(self):
388403
389404 def _get_key (self , event ):
390405 event_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):
433448 if timeout > 0 :
434449 timer = QtCore .QTimer .singleShot (int (timeout * 1000 ),
435450 event_loop .quit )
436- event_loop . exec_ ( )
451+ qt_compat . _exec ( event_loop )
437452
438453 def 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 )
579594 self .canvas .setFocus ()
580595
581596 self .window .raise_ ()
@@ -654,8 +669,8 @@ class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar):
654669 def __init__ (self , canvas , parent , coordinates = True ):
655670 """coordinates: should we show the coordinates on the right?"""
656671 QtWidgets .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
660675 self .coordinates = coordinates
661676 self ._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.
678693 if self .coordinates :
679694 self .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 ) )
682697 self .locLabel .setSizePolicy (
683- QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Expanding ,
684- QtWidgets .QSizePolicy .Ignored ))
698+ QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Policy . Expanding ,
699+ QtWidgets .QSizePolicy .Policy . Ignored ))
685700 labelAction = self .addWidget (self .locLabel )
686701 labelAction .setVisible (True )
687702
@@ -715,7 +730,7 @@ def _icon(self, name):
715730 if self .palette ().color (self .backgroundRole ()).value () < 128 :
716731 icon_color = self .palette ().color (self .foregroundRole ())
717732 mask = pm .createMaskFromColor (QtGui .QColor ('black' ),
718- QtCore . Qt .MaskOutColor )
733+ Qt . MaskMode .MaskOutColor )
719734 pm .fill (icon_color )
720735 pm .setMask (mask )
721736 return QtGui .QIcon (pm )
@@ -785,7 +800,7 @@ def configure_subplots(self):
785800 image = str (cbook ._get_data_path ('images/matplotlib.png' ))
786801 dia = SubplotToolQt (self .canvas .figure , self .canvas .parent ())
787802 dia .setWindowIcon (QtGui .QIcon (image ))
788- dia . exec_ ( )
803+ qt_compat . _exec ( dia )
789804
790805 def save_figure (self , * args ):
791806 filetypes = self .canvas .get_supported_filetypes_grouped ()
@@ -874,7 +889,7 @@ def _export_values(self):
874889 QtGui .QFontMetrics (text .document ().defaultFont ())
875890 .size (0 , text .toPlainText ()).height () + 20 )
876891 text .setMaximumSize (size )
877- dialog . exec_ ( )
892+ qt_compat . _exec ( dialog )
878893
879894 def _on_value_changed (self ):
880895 self ._figure .subplots_adjust (** {attr : self ._widgets [attr ].value ()
@@ -899,14 +914,14 @@ class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar):
899914 def __init__ (self , toolmanager , parent ):
900915 ToolContainerBase .__init__ (self , toolmanager )
901916 QtWidgets .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 ) )
904919 message_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 ) )
907922 message_label .setSizePolicy (
908- QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Expanding ,
909- QtWidgets .QSizePolicy .Ignored ))
923+ QtWidgets .QSizePolicy (QtWidgets .QSizePolicy .Policy . Expanding ,
924+ QtWidgets .QSizePolicy .Policy . Ignored ))
910925 self ._message_action = self .addWidget (message_label )
911926 self ._toolitems = {}
912927 self ._groups = {}
@@ -1031,7 +1046,7 @@ def mainloop():
10311046 if is_python_signal_handler :
10321047 signal .signal (signal .SIGINT , signal .SIG_DFL )
10331048 try :
1034- qApp . exec_ ( )
1049+ qt_compat . _exec ( qApp )
10351050 finally :
10361051 # reset the SIGINT exception handler
10371052 if is_python_signal_handler :
0 commit comments