15
15
from matplotlib .backends .qt_editor ._formsubplottool import UiSubplotTool
16
16
from . import qt_compat
17
17
from .qt_compat import (
18
- QtCore , QtGui , QtWidgets , __version__ , QT_API ,
18
+ QtCore , QtGui , QtWidgets , Qt , __version__ , QT_API ,
19
19
_devicePixelRatioF , _isdeleted , _setDevicePixelRatio ,
20
20
)
21
21
22
22
backend_version = __version__
23
23
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
+
24
27
# SPECIAL_KEYS are keys that do *not* return their unicode name
25
28
# 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
+ }
60
65
if sys .platform == 'darwin' :
61
66
# in OSX, the control and super (aka cmd/apple) keys are switched, so
62
67
# 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
+ })
66
72
# 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.
68
74
# Order determines the modifier order (ctrl+alt+...) reported by Matplotlib.
69
75
_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 ),
74
80
]
75
81
cursord = {
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
+ }
82
88
SUPER = 0 # Deprecated.
83
89
ALT = 1 # Deprecated.
84
90
CTRL = 2 # Deprecated.
87
93
(SPECIAL_KEYS [key ], mod , key ) for mod , key in _MODIFIER_KEYS ]
88
94
89
95
96
+ def _to_int (x ):
97
+ return x .value if QT_API == "PyQt6" else int (x )
98
+
99
+
90
100
# make place holder
91
101
qApp = None
92
102
@@ -140,17 +150,17 @@ def _allow_super_init(__init__):
140
150
Decorator for ``__init__`` to allow ``super().__init__`` on PyQt4/PySide2.
141
151
"""
142
152
143
- if QT_API == "PyQt5" :
153
+ if QT_API in [ "PyQt5" , "PyQt6" ] :
144
154
145
155
return __init__
146
156
147
157
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
150
160
# patch QWidget.__init__ by a cooperative version, that first calls
151
161
# QWidget.__init__ with no additional arguments, and then finds the
152
162
# 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}
154
164
# or Shiboken packages), and manually call its `__init__`, once again
155
165
# passing the additional arguments.
156
166
@@ -162,7 +172,9 @@ def cooperative_qwidget_init(self, *args, **kwargs):
162
172
next_coop_init = next (
163
173
cls for cls in mro [mro .index (QtWidgets .QWidget ) + 1 :]
164
174
if cls .__module__ .split ("." )[0 ] not in [
165
- "PyQt4" , "sip" , "PySide" , "PySide2" , "Shiboken" ])
175
+ "PyQt4" , "sip" ,
176
+ "PySide" , "PySide2" , "PySide6" , "Shiboken" ,
177
+ ])
166
178
next_coop_init .__init__ (self , * args , ** kwargs )
167
179
168
180
@functools .wraps (__init__ )
@@ -207,13 +219,13 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
207
219
required_interactive_framework = "qt5"
208
220
_timer_cls = TimerQT
209
221
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
+ }
217
229
218
230
@_allow_super_init
219
231
def __init__ (self , figure ):
@@ -233,11 +245,11 @@ def __init__(self, figure):
233
245
self ._is_drawing = False
234
246
self ._draw_rect_callback = lambda painter : None
235
247
236
- self .setAttribute (QtCore . Qt .WA_OpaquePaintEvent )
248
+ self .setAttribute (Qt . WidgetAttribute .WA_OpaquePaintEvent )
237
249
self .setMouseTracking (True )
238
250
self .resize (* self .get_width_height ())
239
251
240
- palette = QtGui .QPalette (QtCore . Qt . white )
252
+ palette = QtGui .QPalette (QtGui . QColor ( " white" ) )
241
253
self .setPalette (palette )
242
254
243
255
def _update_figure_dpi (self ):
@@ -311,14 +323,14 @@ def mouseEventCoords(self, pos):
311
323
312
324
def mousePressEvent (self , event ):
313
325
x , y = self .mouseEventCoords (event .pos ())
314
- button = self .buttond .get (event .button ())
326
+ button = self .buttond .get (_to_int ( event .button () ))
315
327
if button is not None :
316
328
FigureCanvasBase .button_press_event (self , x , y , button ,
317
329
guiEvent = event )
318
330
319
331
def mouseDoubleClickEvent (self , event ):
320
332
x , y = self .mouseEventCoords (event .pos ())
321
- button = self .buttond .get (event .button ())
333
+ button = self .buttond .get (_to_int ( event .button () ))
322
334
if button is not None :
323
335
FigureCanvasBase .button_press_event (self , x , y ,
324
336
button , dblclick = True ,
@@ -330,7 +342,7 @@ def mouseMoveEvent(self, event):
330
342
331
343
def mouseReleaseEvent (self , event ):
332
344
x , y = self .mouseEventCoords (event )
333
- button = self .buttond .get (event .button ())
345
+ button = self .buttond .get (_to_int ( event .button () ))
334
346
if button is not None :
335
347
FigureCanvasBase .button_release_event (self , x , y , button ,
336
348
guiEvent = event )
@@ -368,6 +380,9 @@ def keyReleaseEvent(self, event):
368
380
FigureCanvasBase .key_release_event (self , key , guiEvent = event )
369
381
370
382
def resizeEvent (self , event ):
383
+ frame = sys ._getframe ()
384
+ if frame .f_code is frame .f_back .f_code : # Prevent PyQt6 recursion.
385
+ return
371
386
w = event .size ().width () * self ._dpi_ratio
372
387
h = event .size ().height () * self ._dpi_ratio
373
388
dpival = self .figure .dpi
@@ -388,7 +403,7 @@ def minumumSizeHint(self):
388
403
389
404
def _get_key (self , event ):
390
405
event_key = event .key ()
391
- event_mods = int (event .modifiers ()) # actually a bitmask
406
+ event_mods = _to_int (event .modifiers ()) # actually a bitmask
392
407
393
408
# get names of the pressed modifier keys
394
409
# 'control' is named 'control' when a standalone key, but 'ctrl' when a
@@ -433,7 +448,7 @@ def start_event_loop(self, timeout=0):
433
448
if timeout > 0 :
434
449
timer = QtCore .QTimer .singleShot (int (timeout * 1000 ),
435
450
event_loop .quit )
436
- event_loop .exec_ ()
451
+ event_loop .exec ()
437
452
438
453
def stop_event_loop (self , event = None ):
439
454
# docstring inherited
@@ -575,7 +590,7 @@ def __init__(self, canvas, num):
575
590
# StrongFocus accepts both tab and click to focus and will enable the
576
591
# canvas to process event without clicking.
577
592
# https://doc.qt.io/qt-5/qt.html#FocusPolicy-enum
578
- self .canvas .setFocusPolicy (QtCore . Qt .StrongFocus )
593
+ self .canvas .setFocusPolicy (Qt . FocusPolicy .StrongFocus )
579
594
self .canvas .setFocus ()
580
595
581
596
self .window .raise_ ()
@@ -654,8 +669,8 @@ class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar):
654
669
def __init__ (self , canvas , parent , coordinates = True ):
655
670
"""coordinates: should we show the coordinates on the right?"""
656
671
QtWidgets .QToolBar .__init__ (self , parent )
657
- self .setAllowedAreas (
658
- QtCore . Qt .TopToolBarArea | QtCore . Qt . BottomToolBarArea )
672
+ self .setAllowedAreas ( # Qt::TopToolBarArea | Qt::BottomToolBarArea
673
+ Qt .ToolBarAreas ( 0x4 | 0x8 ) )
659
674
660
675
self .coordinates = coordinates
661
676
self ._actions = {} # mapping of toolitem method names to QActions.
@@ -677,11 +692,11 @@ def __init__(self, canvas, parent, coordinates=True):
677
692
# will resize this label instead of the buttons.
678
693
if self .coordinates :
679
694
self .locLabel = QtWidgets .QLabel ("" , self )
680
- self .locLabel .setAlignment (
681
- QtCore . Qt .AlignRight | QtCore . Qt . AlignVCenter )
695
+ self .locLabel .setAlignment ( # Qt::AlignRight | Qt::AlignVCenter
696
+ Qt .Alignment ( 0x02 | 0x80 ) )
682
697
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 ))
685
700
labelAction = self .addWidget (self .locLabel )
686
701
labelAction .setVisible (True )
687
702
@@ -715,7 +730,7 @@ def _icon(self, name):
715
730
if self .palette ().color (self .backgroundRole ()).value () < 128 :
716
731
icon_color = self .palette ().color (self .foregroundRole ())
717
732
mask = pm .createMaskFromColor (QtGui .QColor ('black' ),
718
- QtCore . Qt .MaskOutColor )
733
+ Qt . MaskMode .MaskOutColor )
719
734
pm .fill (icon_color )
720
735
pm .setMask (mask )
721
736
return QtGui .QIcon (pm )
@@ -785,7 +800,7 @@ def configure_subplots(self):
785
800
image = str (cbook ._get_data_path ('images/matplotlib.png' ))
786
801
dia = SubplotToolQt (self .canvas .figure , self .canvas .parent ())
787
802
dia .setWindowIcon (QtGui .QIcon (image ))
788
- dia .exec_ ()
803
+ dia .exec ()
789
804
790
805
def save_figure (self , * args ):
791
806
filetypes = self .canvas .get_supported_filetypes_grouped ()
@@ -874,7 +889,7 @@ def _export_values(self):
874
889
QtGui .QFontMetrics (text .document ().defaultFont ())
875
890
.size (0 , text .toPlainText ()).height () + 20 )
876
891
text .setMaximumSize (size )
877
- dialog .exec_ ()
892
+ dialog .exec ()
878
893
879
894
def _on_value_changed (self ):
880
895
self ._figure .subplots_adjust (** {attr : self ._widgets [attr ].value ()
@@ -899,14 +914,14 @@ class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar):
899
914
def __init__ (self , toolmanager , parent ):
900
915
ToolContainerBase .__init__ (self , toolmanager )
901
916
QtWidgets .QToolBar .__init__ (self , parent )
902
- self .setAllowedAreas (
903
- QtCore . Qt .TopToolBarArea | QtCore . Qt . BottomToolBarArea )
917
+ self .setAllowedAreas ( # Qt::TopToolBarArea | Qt::BottomToolBarArea
918
+ Qt .ToolBarAreas ( 0x4 | 0x8 ) )
904
919
message_label = QtWidgets .QLabel ("" )
905
- message_label .setAlignment (
906
- QtCore . Qt .AlignRight | QtCore . Qt . AlignVCenter )
920
+ message_label .setAlignment ( # Qt::AlignRight | Qt::AlignVCenter
921
+ Qt .Alignment ( 0x02 | 0x80 ) )
907
922
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 ))
910
925
self ._message_action = self .addWidget (message_label )
911
926
self ._toolitems = {}
912
927
self ._groups = {}
@@ -1031,7 +1046,7 @@ def mainloop():
1031
1046
if is_python_signal_handler :
1032
1047
signal .signal (signal .SIGINT , signal .SIG_DFL )
1033
1048
try :
1034
- qApp .exec_ ()
1049
+ qApp .exec ()
1035
1050
finally :
1036
1051
# reset the SIGINT exception handler
1037
1052
if is_python_signal_handler :
0 commit comments