Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf61c7d4

Browse files
committed
Add "experimental" support for PyQt6/PySide6.
Currently these must be selected via `QT_API=pyqt6`/`QT_API=pyside6`.Note that I didn't create a separate backend_qt6agg (andbackend_qt6cairo, and mplcairo.qt6...) as it seems preferable to insteadmove towards a single backend_qtagg and allow selection of the actual qtbinding via the orthogonal QT_API mechanism.Most of the work is just handling attributes that moved out of the Qtnamespace.
1 parentbf0f6cc commitf61c7d4

File tree

6 files changed

+206
-145
lines changed

6 files changed

+206
-145
lines changed

‎lib/matplotlib/backends/backend_qt5.py‎

Lines changed: 112 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
importfunctools
22
importimportlib
3+
importoperator
34
importos
45
importsignal
56
importsys
@@ -15,70 +16,77 @@
1516
frommatplotlib.backends.qt_editor._formsubplottoolimportUiSubplotTool
1617
from .importqt_compat
1718
from .qt_compatimport (
18-
QtCore,QtGui,QtWidgets,__version__,QT_API,
19+
QtCore,QtGui,QtWidgets,Qt,__version__,QT_API,
1920
_devicePixelRatioF,_isdeleted,_setDevicePixelRatio,
2021
)
2122

2223
backend_version=__version__
2324

24-
# SPECIAL_KEYS are keys that do *not* return their unicode name
25-
# 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', }
25+
# Enums are specified using numeric values because 1) they are only available
26+
# as enum attributes on PyQt6 and only available as Qt attributes on PyQt<5.11;
27+
# 2) Foos = QFlags<Foo> is exported as Qt.Foos on PyQt6 but Qt.Foo on PyQt5.
28+
29+
# SPECIAL_KEYS are Qt::Key that do *not* return their unicode name
30+
# instead they have manually specified names.
31+
SPECIAL_KEYS= {
32+
0x1000000:'escape',
33+
0x1000001:'tab',
34+
0x1000003:'backspace',
35+
0x1000004:'enter',
36+
0x1000005:'enter',
37+
0x1000006:'insert',
38+
0x1000007:'delete',
39+
0x1000008:'pause',
40+
0x100000a:'sysreq',
41+
0x100000b:'clear',
42+
0x1000010:'home',
43+
0x1000011:'end',
44+
0x1000012:'left',
45+
0x1000013:'up',
46+
0x1000014:'right',
47+
0x1000015:'down',
48+
0x1000016:'pageup',
49+
0x1000017:'pagedown',
50+
0x1000020:'shift',
51+
0x1000021:'control',
52+
0x1000022:'super',
53+
0x1000023:'alt',
54+
0x1000030:'f1',
55+
0x1000031:'f2',
56+
0x1000032:'f3',
57+
0x1000033:'f4',
58+
0x1000034:'f5',
59+
0x1000035:'f6',
60+
0x1000036:'f7',
61+
0x1000037:'f8',
62+
0x1000038:'f9',
63+
0x1000039:'f10',
64+
0x100003a:'f11',
65+
0x100003b:'f12',
66+
}
6067
ifsys.platform=='darwin':
6168
# in OSX, the control and super (aka cmd/apple) keys are switched, so
6269
# switch them back.
63-
SPECIAL_KEYS.update({QtCore.Qt.Key_Control:'cmd',# cmd/apple key
64-
QtCore.Qt.Key_Meta:'control',
65-
})
70+
SPECIAL_KEYS.update({
71+
0x01000021:'cmd',# cmd/apple key
72+
0x01000022:'control',
73+
})
6674
# Define which modifier keys are collected on keyboard events.
67-
# Elements are (Modifier Flag, QtKey) tuples.
75+
# Elements are (Qt::KeyboardModifier(s), Qt::Key) tuples.
6876
# Order determines the modifier order (ctrl+alt+...) reported by Matplotlib.
6977
_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),
78+
(0x02000000,0x01000020),# shift
79+
(0x04000000,0x01000021),# control
80+
(0x08000000,0x01000023),# alt
81+
(0x10000000,0x01000022),# meta
7482
]
7583
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-
}
84+
cursors.MOVE:Qt.CursorShape(9),# SizeAllCursor
85+
cursors.HAND:Qt.CursorShape(13),# PointingHandCursor
86+
cursors.POINTER:Qt.CursorShape(0),# ArrowCursor
87+
cursors.SELECT_REGION:Qt.CursorShape(2),# CrossCursor
88+
cursors.WAIT:Qt.CursorShape(3),# WaitCursor
89+
}
8290
SUPER=0# Deprecated.
8391
ALT=1# Deprecated.
8492
CTRL=2# Deprecated.
@@ -87,6 +95,10 @@
8795
(SPECIAL_KEYS[key],mod,key)formod,keyin_MODIFIER_KEYS]
8896

8997

98+
def_to_int(x):
99+
returnx.valueifQT_API=="PyQt6"elseint(x)
100+
101+
90102
# make place holder
91103
qApp=None
92104

@@ -140,17 +152,17 @@ def _allow_super_init(__init__):
140152
Decorator for ``__init__`` to allow ``super().__init__`` on PyQt4/PySide2.
141153
"""
142154

143-
ifQT_API=="PyQt5":
155+
ifQT_APIin ["PyQt5","PyQt6"]:
144156

145157
return__init__
146158

147159
else:
148-
# To work around lack of cooperative inheritance in PyQt4, PySide,
149-
#and PySide2, when calling FigureCanvasQT.__init__, we temporarily
160+
# To work around lack of cooperative inheritance in PyQt4 and
161+
#PySide{,2,6}, when calling FigureCanvasQT.__init__, we temporarily
150162
# patch QWidget.__init__ by a cooperative version, that first calls
151163
# QWidget.__init__ with no additional arguments, and then finds the
152164
# next class in the MRO with an __init__ that does support cooperative
153-
# inheritance (i.e., not defined by the PyQt4,PySide, PySide2, sip
165+
# inheritance (i.e., not defined by the PyQt4 or sip, orPySide{,2,6}
154166
# or Shiboken packages), and manually call its `__init__`, once again
155167
# passing the additional arguments.
156168

@@ -162,7 +174,9 @@ def cooperative_qwidget_init(self, *args, **kwargs):
162174
next_coop_init=next(
163175
clsforclsinmro[mro.index(QtWidgets.QWidget)+1:]
164176
ifcls.__module__.split(".")[0]notin [
165-
"PyQt4","sip","PySide","PySide2","Shiboken"])
177+
"PyQt4","sip",
178+
"PySide","PySide2","PySide6","Shiboken",
179+
])
166180
next_coop_init.__init__(self,*args,**kwargs)
167181

168182
@functools.wraps(__init__)
@@ -207,13 +221,13 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
207221
required_interactive_framework="qt5"
208222
_timer_cls=TimerQT
209223

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-
}
224+
buttond= {# Map Qt::MouseButton(s) to MouseEvents.
225+
0x01:MouseButton.LEFT,
226+
0x02:MouseButton.RIGHT,
227+
0x04:MouseButton.MIDDLE,
228+
0x08:MouseButton.BACK,
229+
0x10:MouseButton.FORWARD,
230+
}
217231

218232
@_allow_super_init
219233
def__init__(self,figure):
@@ -233,11 +247,11 @@ def __init__(self, figure):
233247
self._is_drawing=False
234248
self._draw_rect_callback=lambdapainter:None
235249

236-
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
250+
self.setAttribute(4)#Qt.WidgetAttribute.WA_OpaquePaintEvent
237251
self.setMouseTracking(True)
238252
self.resize(*self.get_width_height())
239253

240-
palette=QtGui.QPalette(QtCore.Qt.white)
254+
palette=QtGui.QPalette(QtGui.QColor("white"))
241255
self.setPalette(palette)
242256

243257
def_update_figure_dpi(self):
@@ -283,7 +297,7 @@ def get_width_height(self):
283297

284298
defenterEvent(self,event):
285299
try:
286-
x,y=self.mouseEventCoords(event.pos())
300+
x,y=self.mouseEventCoords(self._get_position(event))
287301
exceptAttributeError:
288302
# the event from PyQt4 does not include the position
289303
x=y=None
@@ -293,6 +307,9 @@ def leaveEvent(self, event):
293307
QtWidgets.QApplication.restoreOverrideCursor()
294308
FigureCanvasBase.leave_notify_event(self,guiEvent=event)
295309

310+
_get_position=operator.methodcaller(
311+
"position"ifQT_APIin ["PyQt6","PySide6"]else"pos")
312+
296313
defmouseEventCoords(self,pos):
297314
"""
298315
Calculate mouse coordinates in physical pixels.
@@ -310,34 +327,34 @@ def mouseEventCoords(self, pos):
310327
returnx*dpi_ratio,y*dpi_ratio
311328

312329
defmousePressEvent(self,event):
313-
x,y=self.mouseEventCoords(event.pos())
314-
button=self.buttond.get(event.button())
330+
x,y=self.mouseEventCoords(self._get_position(event))
331+
button=self.buttond.get(_to_int(event.button()))
315332
ifbuttonisnotNone:
316333
FigureCanvasBase.button_press_event(self,x,y,button,
317334
guiEvent=event)
318335

319336
defmouseDoubleClickEvent(self,event):
320-
x,y=self.mouseEventCoords(event.pos())
321-
button=self.buttond.get(event.button())
337+
x,y=self.mouseEventCoords(self._get_position(event))
338+
button=self.buttond.get(_to_int(event.button()))
322339
ifbuttonisnotNone:
323340
FigureCanvasBase.button_press_event(self,x,y,
324341
button,dblclick=True,
325342
guiEvent=event)
326343

327344
defmouseMoveEvent(self,event):
328-
x,y=self.mouseEventCoords(event)
345+
x,y=self.mouseEventCoords(self._get_position(event))
329346
FigureCanvasBase.motion_notify_event(self,x,y,guiEvent=event)
330347

331348
defmouseReleaseEvent(self,event):
332-
x,y=self.mouseEventCoords(event)
333-
button=self.buttond.get(event.button())
349+
x,y=self.mouseEventCoords(self._get_position(event))
350+
button=self.buttond.get(_to_int(event.button()))
334351
ifbuttonisnotNone:
335352
FigureCanvasBase.button_release_event(self,x,y,button,
336353
guiEvent=event)
337354

338355
ifQtCore.qVersion()>="5.":
339356
defwheelEvent(self,event):
340-
x,y=self.mouseEventCoords(event)
357+
x,y=self.mouseEventCoords(self._get_position(event))
341358
# from QWheelEvent::delta doc
342359
ifevent.pixelDelta().x()==0andevent.pixelDelta().y()==0:
343360
steps=event.angleDelta().y()/120
@@ -368,6 +385,9 @@ def keyReleaseEvent(self, event):
368385
FigureCanvasBase.key_release_event(self,key,guiEvent=event)
369386

370387
defresizeEvent(self,event):
388+
frame=sys._getframe()
389+
ifframe.f_codeisframe.f_back.f_code:# Prevent PyQt6 recursion.
390+
return
371391
w=event.size().width()*self._dpi_ratio
372392
h=event.size().height()*self._dpi_ratio
373393
dpival=self.figure.dpi
@@ -388,7 +408,7 @@ def minumumSizeHint(self):
388408

389409
def_get_key(self,event):
390410
event_key=event.key()
391-
event_mods=int(event.modifiers())# actually a bitmask
411+
event_mods=_to_int(event.modifiers())# actually a bitmask
392412

393413
# get names of the pressed modifier keys
394414
# 'control' is named 'control' when a standalone key, but 'ctrl' when a
@@ -433,7 +453,7 @@ def start_event_loop(self, timeout=0):
433453
iftimeout>0:
434454
timer=QtCore.QTimer.singleShot(int(timeout*1000),
435455
event_loop.quit)
436-
event_loop.exec_()
456+
qt_compat._exec(event_loop)
437457

438458
defstop_event_loop(self,event=None):
439459
# docstring inherited
@@ -575,7 +595,7 @@ def __init__(self, canvas, num):
575595
# StrongFocus accepts both tab and click to focus and will enable the
576596
# canvas to process event without clicking.
577597
# https://doc.qt.io/qt-5/qt.html#FocusPolicy-enum
578-
self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
598+
self.canvas.setFocusPolicy(0x1|0x2|0x8)# StrongFocus
579599
self.canvas.setFocus()
580600

581601
self.window.raise_()
@@ -654,8 +674,8 @@ class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar):
654674
def__init__(self,canvas,parent,coordinates=True):
655675
"""coordinates: should we show the coordinates on the right?"""
656676
QtWidgets.QToolBar.__init__(self,parent)
657-
self.setAllowedAreas(
658-
QtCore.Qt.TopToolBarArea|QtCore.Qt.BottomToolBarArea)
677+
self.setAllowedAreas(# Qt::TopToolBarArea | BottomToolBarArea
678+
Qt.ToolBarAreas(0x4|0x8))
659679

660680
self.coordinates=coordinates
661681
self._actions= {}# mapping of toolitem method names to QActions.
@@ -677,11 +697,10 @@ def __init__(self, canvas, parent, coordinates=True):
677697
# will resize this label instead of the buttons.
678698
ifself.coordinates:
679699
self.locLabel=QtWidgets.QLabel("",self)
680-
self.locLabel.setAlignment(
681-
QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
700+
self.locLabel.setAlignment(# Qt::AlignRight | AlignVCenter
701+
Qt.Alignment(0x02|0x80))
682702
self.locLabel.setSizePolicy(
683-
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
684-
QtWidgets.QSizePolicy.Ignored))
703+
QtWidgets.QSizePolicy(7,8))# Expanding, Ignored
685704
labelAction=self.addWidget(self.locLabel)
686705
labelAction.setVisible(True)
687706

@@ -714,8 +733,8 @@ def _icon(self, name):
714733
_setDevicePixelRatio(pm,_devicePixelRatioF(self))
715734
ifself.palette().color(self.backgroundRole()).value()<128:
716735
icon_color=self.palette().color(self.foregroundRole())
717-
mask=pm.createMaskFromColor(QtGui.QColor('black'),
718-
QtCore.Qt.MaskOutColor)
736+
mask=pm.createMaskFromColor(
737+
QtGui.QColor('black'),1)#Qt.MaskMode.MaskOutColor
719738
pm.fill(icon_color)
720739
pm.setMask(mask)
721740
returnQtGui.QIcon(pm)
@@ -785,7 +804,7 @@ def configure_subplots(self):
785804
image=str(cbook._get_data_path('images/matplotlib.png'))
786805
dia=SubplotToolQt(self.canvas.figure,self.canvas.parent())
787806
dia.setWindowIcon(QtGui.QIcon(image))
788-
dia.exec_()
807+
qt_compat._exec(dia)
789808

790809
defsave_figure(self,*args):
791810
filetypes=self.canvas.get_supported_filetypes_grouped()
@@ -874,7 +893,7 @@ def _export_values(self):
874893
QtGui.QFontMetrics(text.document().defaultFont())
875894
.size(0,text.toPlainText()).height()+20)
876895
text.setMaximumSize(size)
877-
dialog.exec_()
896+
qt_compat._exec(dialog)
878897

879898
def_on_value_changed(self):
880899
self._figure.subplots_adjust(**{attr:self._widgets[attr].value()
@@ -899,14 +918,13 @@ class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar):
899918
def__init__(self,toolmanager,parent):
900919
ToolContainerBase.__init__(self,toolmanager)
901920
QtWidgets.QToolBar.__init__(self,parent)
902-
self.setAllowedAreas(
903-
QtCore.Qt.TopToolBarArea|QtCore.Qt.BottomToolBarArea)
921+
self.setAllowedAreas(# Qt::TopToolBarArea | BottomToolBarArea
922+
Qt.ToolBarAreas(0x4|0x8))
904923
message_label=QtWidgets.QLabel("")
905-
message_label.setAlignment(
906-
QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
924+
message_label.setAlignment(# Qt::AlignRight | AlignVCenter
925+
Qt.Alignment(0x02|0x80))
907926
message_label.setSizePolicy(
908-
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
909-
QtWidgets.QSizePolicy.Ignored))
927+
QtWidgets.QSizePolicy(7,8))# Expanding, Ignored
910928
self._message_action=self.addWidget(message_label)
911929
self._toolitems= {}
912930
self._groups= {}
@@ -1031,7 +1049,7 @@ def mainloop():
10311049
ifis_python_signal_handler:
10321050
signal.signal(signal.SIGINT,signal.SIG_DFL)
10331051
try:
1034-
qApp.exec_()
1052+
qt_compat._exec(qApp)
10351053
finally:
10361054
# reset the SIGINT exception handler
10371055
ifis_python_signal_handler:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp