spyder-ide / qtpy

Provides an uniform layer to support PyQt5, PySide2, PyQt6, PySide6 with a single codebase
MIT License
966 stars 152 forks source link

Missing `QUndoStack` import mapping (Qt5 `QtWidgets` vs Qt6 `QtGui` ) #490

Open th3w1zard1 opened 3 months ago

th3w1zard1 commented 3 months ago

Have ran into an issue where I'm not getting expected imports from the expected module.

import qtpy
if qtpy.API_NAME in ("PyQt5", "PySide2"):
    from qtpy.QtWidgets import QUndoStack
elif qtpy.API_NAME in ("PyQt6", "PySide6"):
    from qtpy.QtGui import QUndoStack
else:
    raise ValueError(f"Invalid QT_API: '{qtpy.API_NAME}'")

is this something qtpy could explain/fix?

th3w1zard1 commented 3 months ago

I was going to create a new issue for each but it seems I have a bunch of small ones:

Issue 1:

        self._proxyModel = QSortFilterProxyModel(self)
        self._proxyModel.setSourceModel(self)
        self._proxyModel.setRecursiveFilteringEnabled(True)  # type: ignore[arg-type]
        self._proxyModel.setFilterCaseSensitivity(False if qtpy.API_NAME in ("PyQt5", "PySide2") else QtCore.Qt.CaseInsensitive)  # type: ignore[arg-type]

raises an exception and includes the phrase 'incorrect signature' in the message.

Issue 2:

if qtpy.API_NAME in ("PyQt6", "PySide6"):
    ...
else:
    from qtpy.QtWidgets import QDesktopWidget

Would love to know the correct place to import QDesktopWidget from in the above?

Issue 3:

def SomeWindow(QMainWindow):
    def __init__(*args, **kwargs):
        if qtpy.API_NAME in {"PySide2", "PyQt5"}:
            self.player.error.connect(lambda _=None: self.handleError())
        else:
            self.player.errorOccurred.connect(lambda *args, **kwargs: self.handleError(*args, **kwargs))
        if qtpy.API_NAME in ["PyQt5", "PySide2"]:
            # PyQt5 and PySide2 code path
            from qtpy.QtMultimedia import QMediaContent

            def set_media(data: bytes | None):
                if data:
                    self.buffer = QBuffer(self)
                    self.buffer.setData(data)
                    self.buffer.open(QIODevice.ReadOnly)
                    self.player.setMedia(QMediaContent(), self.buffer)
                    QtCore.QTimer.singleShot(0, self.player.play)
                else:
                    self.blinkWindow()

        elif qtpy.API_NAME in ["PyQt6", "PySide6"]:
            # PyQt6 and PySide6 code path
            def set_media(data: bytes | None):
                if data:
                    self.tempFile = NamedTemporaryFile(delete=False, suffix=".wav")
                    self.tempFile.write(data)
                    self.tempFile.close()
                    print(f"Wrote audioplayer audio data to '{self.tempFile.name}'")
                    self.player.setSource(QUrl.fromLocalFile(self.tempFile.name))
                else:
                    self.blinkWindow()

Spent hours on this but I cannot figure out how to get the media player working correctly in qt6. Works fine in pyside2/pyqt5.

Issue 4:


if qtpy.API_NAME in ("PyQt6", "PySide6"):
    from qtpy.QtCore import QRegularExpression as QRegExp
    from qtpy.QtGui import QRegularExpressionValidator as QRegExpValidator
else:
    from qtpy.QtCore import QRegExp
    from qtpy.QtGui import QRegExpValidator

    def resRefValidator(self) -> QRegExpValidator:
        return QRegExpValidator(QRegExp(r"^[a-zA-Z0-9_]*$"))

I'm 70% certain there's a bigger change here than the names that is required. How exactly do I unify my use of q regex objects?

dalthviz commented 3 weeks ago

Hi @th3w1zard1 and sorry for such a late response! Regarding your initial compatibilty issue related with QUndoStack I would say adding over QtPy the missing import at

https://github.com/spyder-ide/qtpy/blob/0f7b181e45186b568551cf50078e5eeda05e3953/qtpy/QtWidgets.py#L37-L44

https://github.com/spyder-ide/qtpy/blob/0f7b181e45186b568551cf50078e5eeda05e3953/qtpy/QtWidgets.py#L113-L114

https://github.com/spyder-ide/qtpy/blob/0f7b181e45186b568551cf50078e5eeda05e3953/qtpy/QtGui.py#L54-L64

and at

https://github.com/spyder-ide/qtpy/blob/0f7b181e45186b568551cf50078e5eeda05e3953/qtpy/QtGui.py#L109-L119

should be enough to enable usage either by the Qt5 or Qt6 import syntax: from qtpy.QtWidgets import QUndoStack or from qtpy.QtGui import QUndoStack

Related with the other issues you mention, we would need to give a more in depth check to them but the one I know an answer already its issue 2 which basically is that QDesktopWidget was removed over Qt6 (it was marked as deprecated already over Qt5). For more info about alternatives for its functionality you can check https://doc.qt.io/qt-6/widgets-changes-qt6.html#qdesktopwidget-and-qapplication-desktop

Let us know if you would like to help with the QUndoStack import and if the info above helps!