Skip to content

Commit 6ae5759

Browse files
authored
PR: Improve enum to flags aliasing for PyQt6 and PySide6 > 6.3 and QFileDialog static methods kwarg compatibility (#449)
2 parents b5a8bb6 + 335687d commit 6ae5759

File tree

5 files changed

+71
-32
lines changed

5 files changed

+71
-32
lines changed

qtpy/QtCore.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
"""Provides QtCore classes and functions."""
1010
from typing import TYPE_CHECKING
1111

12-
from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6
12+
from packaging.version import parse
13+
14+
from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version
1315
from ._utils import possibly_static_exec, possibly_static_exec_
1416

1517
if PYQT5:
@@ -69,10 +71,11 @@
6971
# Alias for MiddleButton removed in PyQt6 but available in PyQt5, PySide2 and PySide6
7072
Qt.MidButton = Qt.MiddleButton
7173

72-
# Add removed definition for `Qt.ItemFlags` as an alias of `Qt.ItemFlag` as PySide6 does.
74+
# Add removed definition for `Qt.ItemFlags` as an alias of `Qt.ItemFlag`
75+
# passing as default value 0 in the same way PySide6 6.5+ does.
7376
# Note that for PyQt5 and PySide2 those definitions are two different classes
7477
# (one is the flag definition and the other the enum definition)
75-
Qt.ItemFlags = Qt.ItemFlag
78+
Qt.ItemFlags = lambda value=0: Qt.ItemFlag(value)
7679

7780
elif PYSIDE2:
7881
from PySide2.QtCore import *
@@ -117,6 +120,10 @@
117120
QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs)
118121
QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs)
119122

123+
# Passing as default value 0 in the same way PySide6 6.3.2 does for the `Qt.ItemFlags` definition.
124+
if parse(_qt_version) > parse('6.3'):
125+
Qt.ItemFlags = lambda value=0: Qt.ItemFlag(value)
126+
120127
# For issue #153 and updated for issue #305
121128
if PYQT5 or PYQT6:
122129
QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs)

qtpy/QtWidgets.py

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
# -----------------------------------------------------------------------------
88

99
"""Provides widget classes and functions."""
10-
from functools import partialmethod, wraps
10+
from functools import partialmethod
1111

1212
from packaging.version import parse
1313

1414
from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version
15-
from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep
15+
from ._utils import (
16+
add_action,
17+
possibly_static_exec,
18+
getattr_missing_optional_dep,
19+
static_method_kwargs_wrapper
20+
)
1621

1722

1823
_missing_optional_names = {}
@@ -23,22 +28,6 @@ def __getattr__(name):
2328
raise getattr_missing_optional_dep(
2429
name, module_name=__name__, optional_names=_missing_optional_names)
2530

26-
def _dir_to_directory(func):
27-
@wraps(func)
28-
def _dir_to_directory_(*args, **kwargs):
29-
if "dir" in kwargs:
30-
kwargs["directory"] = kwargs.pop("dir")
31-
return func(*args, **kwargs)
32-
return _dir_to_directory_
33-
34-
def _directory_to_dir(func):
35-
@wraps(func)
36-
def _directory_to_dir_(*args, **kwargs):
37-
if "directory" in kwargs:
38-
kwargs["dir"] = kwargs.pop("directory")
39-
return func(*args, **kwargs)
40-
return _directory_to_dir_
41-
4231

4332
if PYQT5:
4433
from PyQt5.QtWidgets import *
@@ -71,8 +60,11 @@ def _directory_to_dir_(*args, **kwargs):
7160
QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs)
7261
QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom())
7362

74-
# Map missing flags definitions
75-
QFileDialog.Options = QFileDialog.Option
63+
# Add removed definition for `QFileDialog.Options` as an alias of `QFileDialog.Option`
64+
# passing as default value 0 in the same way PySide6 6.5+ does.
65+
# Note that for PyQt5 and PySide2 those definitions are two different classes
66+
# (one is the flag definition and the other the enum definition)
67+
QFileDialog.Options = lambda value=0: QFileDialog.Option(value)
7668

7769
# Allow unscoped access for enums inside the QtWidgets module
7870
from .enums_compat import promote_enums
@@ -108,17 +100,39 @@ def _directory_to_dir_(*args, **kwargs):
108100
QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs)
109101
QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs)
110102

103+
# Passing as default value 0 in the same way PySide6 < 6.3.2 does for the `QFileDialog.Options` definition.
104+
if parse(_qt_version) > parse('6.3'):
105+
QFileDialog.Options = lambda value=0: QFileDialog.Option(value)
106+
111107

112108
if PYSIDE2 or PYSIDE6:
113-
QFileDialog.getExistingDirectory = _directory_to_dir(QFileDialog.getExistingDirectory)
114-
QFileDialog.getOpenFileName = _directory_to_dir(QFileDialog.getOpenFileName)
115-
QFileDialog.getOpenFileNames = _directory_to_dir(QFileDialog.getOpenFileNames)
116-
QFileDialog.getSaveFileName = _directory_to_dir(QFileDialog.getSaveFileName)
109+
# Make PySide2/6 `QFileDialog` static methods accept the `directory` kwarg as `dir`
110+
QFileDialog.getExistingDirectory = static_method_kwargs_wrapper(
111+
QFileDialog.getExistingDirectory, "directory", "dir"
112+
)
113+
QFileDialog.getOpenFileName = static_method_kwargs_wrapper(
114+
QFileDialog.getOpenFileName, "directory", "dir"
115+
)
116+
QFileDialog.getOpenFileNames = static_method_kwargs_wrapper(
117+
QFileDialog.getOpenFileNames, "directory", "dir"
118+
)
119+
QFileDialog.getSaveFileName = static_method_kwargs_wrapper(
120+
QFileDialog.getSaveFileName, "directory", "dir"
121+
)
117122
else:
118-
QFileDialog.getExistingDirectory = _dir_to_directory(QFileDialog.getExistingDirectory)
119-
QFileDialog.getOpenFileName = _dir_to_directory(QFileDialog.getOpenFileName)
120-
QFileDialog.getOpenFileNames = _dir_to_directory(QFileDialog.getOpenFileNames)
121-
QFileDialog.getSaveFileName = _dir_to_directory(QFileDialog.getSaveFileName)
123+
# Make PyQt5/6 `QFileDialog` static methods accept the `dir` kwarg as `directory`
124+
QFileDialog.getExistingDirectory = static_method_kwargs_wrapper(
125+
QFileDialog.getExistingDirectory, "dir", "directory"
126+
)
127+
QFileDialog.getOpenFileName = static_method_kwargs_wrapper(
128+
QFileDialog.getOpenFileName, "dir", "directory"
129+
)
130+
QFileDialog.getOpenFileNames = static_method_kwargs_wrapper(
131+
QFileDialog.getOpenFileNames, "dir", "directory"
132+
)
133+
QFileDialog.getSaveFileName = static_method_kwargs_wrapper(
134+
QFileDialog.getSaveFileName, "dir", "directory"
135+
)
122136

123137
# Make `addAction` compatible with Qt6 >= 6.3
124138
if PYQT5 or PYSIDE2 or parse(_qt_version) < parse('6.3'):

qtpy/_utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# -----------------------------------------------------------------------------
77

88
"""Provides utility functions for use by QtPy itself."""
9+
from functools import wraps
910

1011
import qtpy
1112

@@ -112,3 +113,18 @@ def add_action(self, *args, old_add_action):
112113
return old_add_action(self, *args)
113114
return action
114115
return old_add_action(self, *args)
116+
117+
118+
def static_method_kwargs_wrapper(func, from_kwarg_name, to_kwarg_name):
119+
"""
120+
Helper function to manage `from_kwarg_name` to `to_kwarg_name` kwargs name changes in static methods.
121+
122+
Makes static methods accept the `from_kwarg_name` kwarg as `to_kwarg_name`.
123+
"""
124+
@staticmethod
125+
@wraps(func)
126+
def _from_kwarg_name_to_kwarg_name_(*args, **kwargs):
127+
if from_kwarg_name in kwargs:
128+
kwargs[to_kwarg_name] = kwargs.pop(from_kwarg_name)
129+
return func(*args, **kwargs)
130+
return _from_kwarg_name_to_kwarg_name_

qtpy/tests/test_qtcore.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,4 @@ def test_itemflags_typedef():
179179
Test existence of `QFlags<ItemFlag>` typedef `ItemFlags` that was removed from PyQt6
180180
"""
181181
assert QtCore.Qt.ItemFlags is not None
182+
assert QtCore.Qt.ItemFlags() == QtCore.Qt.ItemFlag(0)

qtpy/tests/test_qtwidgets.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,5 @@ def test_qfiledialog_flags_typedef():
236236
"""
237237
Test existence of `QFlags<Option>` typedef `Options` that was removed from PyQt6
238238
"""
239-
assert QtWidgets.QFileDialog.Options is not None
239+
assert QtWidgets.QFileDialog.Options is not None
240+
assert QtWidgets.QFileDialog.Options() == QtWidgets.QFileDialog.Option(0)

0 commit comments

Comments
 (0)