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

Commitca3b175

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).PyQt6 mouse interactions are currently broken because its version ofQMouseEvent doesn't have an `x()` method. I assume that's just anoversight (the method still exists at the C level) and will be fixed ina future bugfix, so we should at least wait a bit for that.
1 parent2631206 commitca3b175

File tree

6 files changed

+197
-134
lines changed

6 files changed

+197
-134
lines changed

‎lib/matplotlib/backends/backend_qt5.py‎

Lines changed: 100 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -15,70 +15,76 @@
1515
frommatplotlib.backends.qt_editor._formsubplottoolimportUiSubplotTool
1616
from .importqt_compat
1717
from .qt_compatimport (
18-
QtCore,QtGui,QtWidgets,__version__,QT_API,
18+
QtCore,QtGui,QtWidgets,Qt,__version__,QT_API,
1919
_devicePixelRatioF,_isdeleted,_setDevicePixelRatio,
2020
)
2121

2222
backend_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+
}
6065
ifsys.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
]
7581
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+
}
8288
SUPER=0# Deprecated.
8389
ALT=1# Deprecated.
8490
CTRL=2# Deprecated.
@@ -87,6 +93,10 @@
8793
(SPECIAL_KEYS[key],mod,key)formod,keyin_MODIFIER_KEYS]
8894

8995

96+
def_to_int(x):
97+
returnx.valueifQT_API=="PyQt6"elseint(x)
98+
99+
90100
# make place holder
91101
qApp=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-
ifQT_API=="PyQt5":
153+
ifQT_APIin ["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, orPySide{,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
clsforclsinmro[mro.index(QtWidgets.QWidget)+1:]
164174
ifcls.__module__.split(".")[0]notin [
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=lambdapainter: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
defmousePressEvent(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
ifbuttonisnotNone:
316328
FigureCanvasBase.button_press_event(self,x,y,button,
317329
guiEvent=event)
318330

319331
defmouseDoubleClickEvent(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
ifbuttonisnotNone:
323335
FigureCanvasBase.button_press_event(self,x,y,
324336
button,dblclick=True,
@@ -330,7 +342,7 @@ def mouseMoveEvent(self, event):
330342

331343
defmouseReleaseEvent(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
ifbuttonisnotNone:
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
defresizeEvent(self,event):
383+
frame=sys._getframe()
384+
ifframe.f_codeisframe.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
iftimeout>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
defstop_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
ifself.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
ifself.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
returnQtGui.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
defsave_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
ifis_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
ifis_python_signal_handler:

‎lib/matplotlib/backends/backend_qt5agg.py‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,20 @@ def paintEvent(self, event):
5959
# clear the widget canvas
6060
painter.eraseRect(rect)
6161

62-
qimage=QtGui.QImage(buf,buf.shape[1],buf.shape[0],
63-
QtGui.QImage.Format_ARGB32_Premultiplied)
62+
ifQT_API=="PyQt6":
63+
fromPyQt6importsip
64+
buf_ptr=sip.voidptr(buf)
65+
else:
66+
buf_ptr=buf
67+
qimage=QtGui.QImage(
68+
buf_ptr,buf.shape[1],buf.shape[0],
69+
QtGui.QImage.Format.Format_ARGB32_Premultiplied)
6470
_setDevicePixelRatio(qimage,self._dpi_ratio)
6571
# set origin using original QT coordinates
6672
origin=QtCore.QPoint(rect.left(),rect.top())
6773
painter.drawImage(origin,qimage)
6874
# Adjust the buf reference count to work around a memory
69-
# leak bug in QImage under PySide on Python 3.
75+
# leak bug in QImage under PySide.
7076
ifQT_APIin ('PySide','PySide2'):
7177
ctypes.c_long.from_address(id(buf)).value=1
7278

‎lib/matplotlib/backends/backend_qt5cairo.py‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,16 @@ def paintEvent(self, event):
2626
self._renderer.set_width_height(width,height)
2727
self.figure.draw(self._renderer)
2828
buf=self._renderer.gc.ctx.get_target().get_data()
29-
qimage=QtGui.QImage(buf,width,height,
30-
QtGui.QImage.Format_ARGB32_Premultiplied)
29+
ifQT_API=="PyQt6":
30+
importsip
31+
buf_ptr=sip.voidptr(buf)
32+
else:
33+
buf_ptr=buf
34+
qimage=QtGui.QImage(buf_ptr,width,height,
35+
QtGui.QImage.Format.Format_ARGB32_Premultiplied)
3136
# Adjust the buf reference count to work around a memory leak bug in
32-
# QImage under PySide on Python 3.
33-
ifQT_API=='PySide':
37+
# QImage under PySide.
38+
ifQT_APIin ('PySide','PySide2'):
3439
ctypes.c_long.from_address(id(buf)).value=1
3540
_setDevicePixelRatio(qimage,dpi_ratio)
3641
painter=QtGui.QPainter(self)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp