Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Improve enum to flags aliasing for PyQt6 and PySide6 > 6.3 and QFileDialog static methods kwarg compatibility #449

Merged
merged 4 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions qtpy/QtCore.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"""Provides QtCore classes and functions."""
from typing import TYPE_CHECKING

from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6
from packaging.version import parse

from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version
from ._utils import possibly_static_exec, possibly_static_exec_

if PYQT5:
Expand Down Expand Up @@ -62,10 +64,11 @@
# Alias for MiddleButton removed in PyQt6 but available in PyQt5, PySide2 and PySide6
Qt.MidButton = Qt.MiddleButton

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

elif PYSIDE2:
from PySide2.QtCore import *
Expand Down Expand Up @@ -110,6 +113,10 @@
QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs)
QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs)

# Passing as default value 0 in the same way PySide6 6.3.2 does for the `Qt.ItemFlags` definition.
if parse(_qt_version) > parse('6.3'):
Qt.ItemFlags = lambda value=0: Qt.ItemFlag(value)

# For issue #153 and updated for issue #305
if PYQT5 or PYQT6:
QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs)
Expand Down
70 changes: 42 additions & 28 deletions qtpy/QtWidgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@
# -----------------------------------------------------------------------------

"""Provides widget classes and functions."""
from functools import partialmethod, wraps
from functools import partialmethod

from packaging.version import parse

from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version
from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep
from ._utils import (
add_action,
possibly_static_exec,
getattr_missing_optional_dep,
static_method_kwargs_wrapper
)


_missing_optional_names = {}
Expand All @@ -23,22 +28,6 @@ def __getattr__(name):
raise getattr_missing_optional_dep(
name, module_name=__name__, optional_names=_missing_optional_names)

def _dir_to_directory(func):
@wraps(func)
def _dir_to_directory_(*args, **kwargs):
if "dir" in kwargs:
kwargs["directory"] = kwargs.pop("dir")
return func(*args, **kwargs)
return _dir_to_directory_

def _directory_to_dir(func):
@wraps(func)
def _directory_to_dir_(*args, **kwargs):
if "directory" in kwargs:
kwargs["dir"] = kwargs.pop("directory")
return func(*args, **kwargs)
return _directory_to_dir_


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

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

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

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


if PYSIDE2 or PYSIDE6:
QFileDialog.getExistingDirectory = _directory_to_dir(QFileDialog.getExistingDirectory)
QFileDialog.getOpenFileName = _directory_to_dir(QFileDialog.getOpenFileName)
QFileDialog.getOpenFileNames = _directory_to_dir(QFileDialog.getOpenFileNames)
QFileDialog.getSaveFileName = _directory_to_dir(QFileDialog.getSaveFileName)
# Make PySide2/6 `QFileDialog` static methods accept the `directory` kwarg as `dir`
QFileDialog.getExistingDirectory = static_method_kwargs_wrapper(
QFileDialog.getExistingDirectory, "directory", "dir"
)
QFileDialog.getOpenFileName = static_method_kwargs_wrapper(
QFileDialog.getOpenFileName, "directory", "dir"
)
QFileDialog.getOpenFileNames = static_method_kwargs_wrapper(
QFileDialog.getOpenFileNames, "directory", "dir"
)
QFileDialog.getSaveFileName = static_method_kwargs_wrapper(
QFileDialog.getSaveFileName, "directory", "dir"
)
else:
QFileDialog.getExistingDirectory = _dir_to_directory(QFileDialog.getExistingDirectory)
QFileDialog.getOpenFileName = _dir_to_directory(QFileDialog.getOpenFileName)
QFileDialog.getOpenFileNames = _dir_to_directory(QFileDialog.getOpenFileNames)
QFileDialog.getSaveFileName = _dir_to_directory(QFileDialog.getSaveFileName)
# Make PyQt5/6 `QFileDialog` static methods accept the `dir` kwarg as `directory`
QFileDialog.getExistingDirectory = static_method_kwargs_wrapper(
QFileDialog.getExistingDirectory, "dir", "directory"
)
QFileDialog.getOpenFileName = static_method_kwargs_wrapper(
QFileDialog.getOpenFileName, "dir", "directory"
)
QFileDialog.getOpenFileNames = static_method_kwargs_wrapper(
QFileDialog.getOpenFileNames, "dir", "directory"
)
QFileDialog.getSaveFileName = static_method_kwargs_wrapper(
QFileDialog.getSaveFileName, "dir", "directory"
)

# Make `addAction` compatible with Qt6 >= 6.3
if PYQT5 or PYSIDE2 or parse(_qt_version) < parse('6.3'):
Expand Down
16 changes: 16 additions & 0 deletions qtpy/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# -----------------------------------------------------------------------------

"""Provides utility functions for use by QtPy itself."""
from functools import wraps

import qtpy

Expand Down Expand Up @@ -112,3 +113,18 @@ def add_action(self, *args, old_add_action):
return old_add_action(self, *args)
return action
return old_add_action(self, *args)


def static_method_kwargs_wrapper(func, from_kwarg_name, to_kwarg_name):
"""
Helper function to manage `from_kwarg_name` to `to_kwarg_name` kwargs name changes in static methods.

Makes static methods accept the `from_kwarg_name` kwarg as `to_kwarg_name`.
"""
@staticmethod
@wraps(func)
def _from_kwarg_name_to_kwarg_name_(*args, **kwargs):
if from_kwarg_name in kwargs:
kwargs[to_kwarg_name] = kwargs.pop(from_kwarg_name)
return func(*args, **kwargs)
return _from_kwarg_name_to_kwarg_name_
1 change: 1 addition & 0 deletions qtpy/tests/test_qtcore.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,4 @@ def test_itemflags_typedef():
Test existence of `QFlags<ItemFlag>` typedef `ItemFlags` that was removed from PyQt6
"""
assert QtCore.Qt.ItemFlags is not None
assert QtCore.Qt.ItemFlags() == QtCore.Qt.ItemFlag(0)
3 changes: 2 additions & 1 deletion qtpy/tests/test_qtwidgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,5 @@ def test_qfiledialog_flags_typedef():
"""
Test existence of `QFlags<Option>` typedef `Options` that was removed from PyQt6
"""
assert QtWidgets.QFileDialog.Options is not None
assert QtWidgets.QFileDialog.Options is not None
assert QtWidgets.QFileDialog.Options() == QtWidgets.QFileDialog.Option(0)