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

Commit40c8bc5

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 (but the new locations are also compatible with Qt5).
1 parent2631206 commit40c8bc5

File tree

7 files changed

+214
-138
lines changed

7 files changed

+214
-138
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Signature change for ``FigureCanvasQT.mouseEventCoords``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
The signature of ``FigureCanvasQT.mouseEventCoords`` has been modified to
4+
accomodate the deprecation of the ``QEvent.pos()``, ``QEvent.x()``, etc.
5+
methods in Qt6. ``mouseEventCoords`` now only accepts ``QEvent``\s as
6+
parameter (whereas it previously also accepted ``QPoint``\s and ``QPointF``\s);
7+
the sole parameter has accordingly been renamed to ``event`` instead of
8+
``pos``.

‎lib/matplotlib/backends/backend_qt5.py‎

Lines changed: 109 additions & 89 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,76 @@
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

25+
# Foos = QFlags<Foo> are exported as Qt.Foos on PyQt6 but Qt.Foo on PyQt5, so
26+
# we just hard-code numeric values for simplicity.
27+
2428
# SPECIAL_KEYS are keys that do *not* return their unicode name
2529
# 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', }
30+
SPECIAL_KEYS= {
31+
Qt.Key.Key_Control:'control',
32+
Qt.Key.Key_Shift:'shift',
33+
Qt.Key.Key_Alt:'alt',
34+
Qt.Key.Key_Meta:'super',
35+
Qt.Key.Key_Return:'enter',
36+
Qt.Key.Key_Left:'left',
37+
Qt.Key.Key_Up:'up',
38+
Qt.Key.Key_Right:'right',
39+
Qt.Key.Key_Down:'down',
40+
Qt.Key.Key_Escape:'escape',
41+
Qt.Key.Key_F1:'f1',
42+
Qt.Key.Key_F2:'f2',
43+
Qt.Key.Key_F3:'f3',
44+
Qt.Key.Key_F4:'f4',
45+
Qt.Key.Key_F5:'f5',
46+
Qt.Key.Key_F6:'f6',
47+
Qt.Key.Key_F7:'f7',
48+
Qt.Key.Key_F8:'f8',
49+
Qt.Key.Key_F9:'f9',
50+
Qt.Key.Key_F10:'f10',
51+
Qt.Key.Key_F11:'f11',
52+
Qt.Key.Key_F12:'f12',
53+
Qt.Key.Key_Home:'home',
54+
Qt.Key.Key_End:'end',
55+
Qt.Key.Key_PageUp:'pageup',
56+
Qt.Key.Key_PageDown:'pagedown',
57+
Qt.Key.Key_Tab:'tab',
58+
Qt.Key.Key_Backspace:'backspace',
59+
Qt.Key.Key_Enter:'enter',
60+
Qt.Key.Key_Insert:'insert',
61+
Qt.Key.Key_Delete:'delete',
62+
Qt.Key.Key_Pause:'pause',
63+
Qt.Key.Key_SysReq:'sysreq',
64+
Qt.Key.Key_Clear:'clear',
65+
}
6066
ifsys.platform=='darwin':
6167
# in OSX, the control and super (aka cmd/apple) keys are switched, so
6268
# switch them back.
63-
SPECIAL_KEYS.update({QtCore.Qt.Key_Control:'cmd',# cmd/apple key
64-
QtCore.Qt.Key_Meta:'control',
65-
})
69+
SPECIAL_KEYS.update({
70+
Qt.Key.Key_Control:'cmd',# cmd/apple key
71+
Qt.Key.Key_Meta:'control',
72+
})
6673
# Define which modifier keys are collected on keyboard events.
67-
# Elements are (Modifier Flag, Qt Key) tuples.
74+
# Elements are (Qt::KeyboardModifier(s), Qt Key) tuples.
6875
# Order determines the modifier order (ctrl+alt+...) reported by Matplotlib.
6976
_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),
77+
(0x02000000,Qt.Key.Key_Shift),
78+
(0x04000000,Qt.Key.Key_Control),
79+
(0x08000000,Qt.Key.Key_Alt),
80+
(0x10000000,Qt.Key.Key_Meta),
7481
]
7582
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-
}
83+
cursors.MOVE:Qt.CursorShape.SizeAllCursor,
84+
cursors.HAND:Qt.CursorShape.PointingHandCursor,
85+
cursors.POINTER:Qt.CursorShape.ArrowCursor,
86+
cursors.SELECT_REGION:Qt.CursorShape.CrossCursor,
87+
cursors.WAIT:Qt.CursorShape.WaitCursor,
88+
}
8289
SUPER=0# Deprecated.
8390
ALT=1# Deprecated.
8491
CTRL=2# Deprecated.
@@ -87,6 +94,10 @@
8794
(SPECIAL_KEYS[key],mod,key)formod,keyin_MODIFIER_KEYS]
8895

8996

97+
def_to_int(x):
98+
returnx.valueifQT_API=="PyQt6"elseint(x)
99+
100+
90101
# make place holder
91102
qApp=None
92103

@@ -140,17 +151,17 @@ def _allow_super_init(__init__):
140151
Decorator for ``__init__`` to allow ``super().__init__`` on PyQt4/PySide2.
141152
"""
142153

143-
ifQT_API=="PyQt5":
154+
ifQT_APIin ["PyQt5","PyQt6"]:
144155

145156
return__init__
146157

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

@@ -162,7 +173,9 @@ def cooperative_qwidget_init(self, *args, **kwargs):
162173
next_coop_init=next(
163174
clsforclsinmro[mro.index(QtWidgets.QWidget)+1:]
164175
ifcls.__module__.split(".")[0]notin [
165-
"PyQt4","sip","PySide","PySide2","Shiboken"])
176+
"PyQt4","sip",
177+
"PySide","PySide2","PySide6","Shiboken",
178+
])
166179
next_coop_init.__init__(self,*args,**kwargs)
167180

168181
@functools.wraps(__init__)
@@ -207,13 +220,13 @@ class FigureCanvasQT(QtWidgets.QWidget, FigureCanvasBase):
207220
required_interactive_framework="qt5"
208221
_timer_cls=TimerQT
209222

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

218231
@_allow_super_init
219232
def__init__(self,figure):
@@ -233,11 +246,11 @@ def __init__(self, figure):
233246
self._is_drawing=False
234247
self._draw_rect_callback=lambdapainter:None
235248

236-
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
249+
self.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent)
237250
self.setMouseTracking(True)
238251
self.resize(*self.get_width_height())
239252

240-
palette=QtGui.QPalette(QtCore.Qt.white)
253+
palette=QtGui.QPalette(QtGui.QColor("white"))
241254
self.setPalette(palette)
242255

243256
def_update_figure_dpi(self):
@@ -283,7 +296,7 @@ def get_width_height(self):
283296

284297
defenterEvent(self,event):
285298
try:
286-
x,y=self.mouseEventCoords(event.pos())
299+
x,y=self.mouseEventCoords(event)
287300
exceptAttributeError:
288301
# the event from PyQt4 does not include the position
289302
x=y=None
@@ -293,7 +306,10 @@ def leaveEvent(self, event):
293306
QtWidgets.QApplication.restoreOverrideCursor()
294307
FigureCanvasBase.leave_notify_event(self,guiEvent=event)
295308

296-
defmouseEventCoords(self,pos):
309+
_get_position=operator.methodcaller(
310+
"position"ifQT_APIin ["PyQt6","PySide6"]else"pos")
311+
312+
defmouseEventCoords(self,event):
297313
"""
298314
Calculate mouse coordinates in physical pixels.
299315
@@ -304,21 +320,22 @@ def mouseEventCoords(self, pos):
304320
Also, the origin is different and needs to be corrected.
305321
"""
306322
dpi_ratio=self._dpi_ratio
323+
pos=self._get_position(event)
307324
x=pos.x()
308325
# flip y so y=0 is bottom of canvas
309326
y=self.figure.bbox.height/dpi_ratio-pos.y()
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(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(event)
338+
button=self.buttond.get(_to_int(event.button()))
322339
ifbuttonisnotNone:
323340
FigureCanvasBase.button_press_event(self,x,y,
324341
button,dblclick=True,
@@ -330,7 +347,7 @@ def mouseMoveEvent(self, event):
330347

331348
defmouseReleaseEvent(self,event):
332349
x,y=self.mouseEventCoords(event)
333-
button=self.buttond.get(event.button())
350+
button=self.buttond.get(_to_int(event.button()))
334351
ifbuttonisnotNone:
335352
FigureCanvasBase.button_release_event(self,x,y,button,
336353
guiEvent=event)
@@ -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(Qt.FocusPolicy.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,11 @@ 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(QtWidgets.QSizePolicy.Policy.Expanding,
704+
QtWidgets.QSizePolicy.Policy.Ignored))
685705
labelAction=self.addWidget(self.locLabel)
686706
labelAction.setVisible(True)
687707

@@ -715,7 +735,7 @@ def _icon(self, name):
715735
ifself.palette().color(self.backgroundRole()).value()<128:
716736
icon_color=self.palette().color(self.foregroundRole())
717737
mask=pm.createMaskFromColor(QtGui.QColor('black'),
718-
QtCore.Qt.MaskOutColor)
738+
Qt.MaskMode.MaskOutColor)
719739
pm.fill(icon_color)
720740
pm.setMask(mask)
721741
returnQtGui.QIcon(pm)
@@ -785,7 +805,7 @@ def configure_subplots(self):
785805
image=str(cbook._get_data_path('images/matplotlib.png'))
786806
dia=SubplotToolQt(self.canvas.figure,self.canvas.parent())
787807
dia.setWindowIcon(QtGui.QIcon(image))
788-
dia.exec_()
808+
qt_compat._exec(dia)
789809

790810
defsave_figure(self,*args):
791811
filetypes=self.canvas.get_supported_filetypes_grouped()
@@ -874,7 +894,7 @@ def _export_values(self):
874894
QtGui.QFontMetrics(text.document().defaultFont())
875895
.size(0,text.toPlainText()).height()+20)
876896
text.setMaximumSize(size)
877-
dialog.exec_()
897+
qt_compat._exec(dialog)
878898

879899
def_on_value_changed(self):
880900
self._figure.subplots_adjust(**{attr:self._widgets[attr].value()
@@ -899,14 +919,14 @@ class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar):
899919
def__init__(self,toolmanager,parent):
900920
ToolContainerBase.__init__(self,toolmanager)
901921
QtWidgets.QToolBar.__init__(self,parent)
902-
self.setAllowedAreas(
903-
QtCore.Qt.TopToolBarArea|QtCore.Qt.BottomToolBarArea)
922+
self.setAllowedAreas(# Qt::TopToolBarArea | BottomToolBarArea
923+
Qt.ToolBarAreas(0x4|0x8))
904924
message_label=QtWidgets.QLabel("")
905-
message_label.setAlignment(
906-
QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
925+
message_label.setAlignment(# Qt::AlignRight | AlignVCenter
926+
Qt.Alignment(0x02|0x80))
907927
message_label.setSizePolicy(
908-
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,
909-
QtWidgets.QSizePolicy.Ignored))
928+
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding,
929+
QtWidgets.QSizePolicy.Policy.Ignored))
910930
self._message_action=self.addWidget(message_label)
911931
self._toolitems= {}
912932
self._groups= {}
@@ -1031,7 +1051,7 @@ def mainloop():
10311051
ifis_python_signal_handler:
10321052
signal.signal(signal.SIGINT,signal.SIG_DFL)
10331053
try:
1034-
qApp.exec_()
1054+
qt_compat._exec(qApp)
10351055
finally:
10361056
# reset the SIGINT exception handler
10371057
ifis_python_signal_handler:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp