diff options
Diffstat (limited to 'qutebrowser/utils/qtutils.py')
-rw-r--r-- | qutebrowser/utils/qtutils.py | 110 |
1 files changed, 87 insertions, 23 deletions
diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index ff8983c50..0ecc4cba6 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -29,22 +29,25 @@ Module attributes: import io +import enum +import pathlib import operator import contextlib from typing import (Any, AnyStr, TYPE_CHECKING, BinaryIO, IO, Iterator, Optional, Union, Tuple, cast) -from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray, +from qutebrowser.qt import machinery, sip +from qutebrowser.qt.core import (qVersion, QEventLoop, QDataStream, QByteArray, QIODevice, QFileDevice, QSaveFile, QT_VERSION_STR, - PYQT_VERSION_STR, QObject, QUrl) -from PyQt5.QtGui import QColor + PYQT_VERSION_STR, QObject, QUrl, QLibraryInfo) +from qutebrowser.qt.gui import QColor try: - from PyQt5.QtWebKit import qWebKitVersion + from qutebrowser.qt.webkit import qWebKitVersion except ImportError: # pragma: no cover qWebKitVersion = None # type: ignore[assignment] # noqa: N816 if TYPE_CHECKING: - from PyQt5.QtWebKit import QWebHistory - from PyQt5.QtWebEngineWidgets import QWebEngineHistory + from qutebrowser.qt.webkit import QWebHistory + from qutebrowser.qt.webenginewidgets import QWebEngineHistory from qutebrowser.misc import objects from qutebrowser.utils import usertypes, utils @@ -173,14 +176,14 @@ def ensure_valid(obj: Validatable) -> None: def check_qdatastream(stream: QDataStream) -> None: """Check the status of a QDataStream and raise OSError if it's not ok.""" status_to_str = { - QDataStream.Ok: "The data stream is operating normally.", - QDataStream.ReadPastEnd: ("The data stream has read past the end of " + QDataStream.Status.Ok: "The data stream is operating normally.", + QDataStream.Status.ReadPastEnd: ("The data stream has read past the end of " "the data in the underlying device."), - QDataStream.ReadCorruptData: "The data stream has read corrupt data.", - QDataStream.WriteFailed: ("The data stream cannot write to the " + QDataStream.Status.ReadCorruptData: "The data stream has read corrupt data.", + QDataStream.Status.WriteFailed: ("The data stream cannot write to the " "underlying device."), } - if stream.status() != QDataStream.Ok: + if stream.status() != QDataStream.Status.Ok: raise OSError(status_to_str[stream.status()]) @@ -196,14 +199,14 @@ _QtSerializableType = Union[ def serialize(obj: _QtSerializableType) -> QByteArray: """Serialize an object into a QByteArray.""" data = QByteArray() - stream = QDataStream(data, QIODevice.WriteOnly) + stream = QDataStream(data, QIODevice.OpenModeFlag.WriteOnly) serialize_stream(stream, obj) return data def deserialize(data: QByteArray, obj: _QtSerializableType) -> None: """Deserialize an object from a QByteArray.""" - stream = QDataStream(data, QIODevice.ReadOnly) + stream = QDataStream(data, QIODevice.OpenModeFlag.ReadOnly) deserialize_stream(stream, obj) @@ -233,7 +236,7 @@ def savefile_open( f = QSaveFile(filename) cancelled = False try: - open_ok = f.open(QIODevice.WriteOnly) + open_ok = f.open(QIODevice.OpenModeFlag.WriteOnly) if not open_ok: raise QtOSError(f) @@ -302,7 +305,7 @@ class PyQIODevice(io.BufferedIOBase): # contextlib.closing is only generic in Python 3.9+ def open( self, - mode: QIODevice.OpenMode, + mode: QIODevice.OpenModeFlag, ) -> contextlib.closing: # type: ignore[type-arg] """Open the underlying device and ensure opening succeeded. @@ -367,7 +370,7 @@ class PyQIODevice(io.BufferedIOBase): self._check_readable() if size is None or size < 0: - qt_size = 0 # no maximum size + qt_size = None # no maximum size elif size == 0: return b'' else: @@ -375,7 +378,10 @@ class PyQIODevice(io.BufferedIOBase): buf: Union[QByteArray, bytes, None] = None if self.dev.canReadLine(): - buf = self.dev.readLine(qt_size) + if qt_size is None: + buf = self.dev.readLine() + else: + buf = self.dev.readLine(qt_size) elif size is None or size < 0: buf = self.dev.readAll() else: @@ -450,6 +456,12 @@ class QtValueError(ValueError): super().__init__(err) +if machinery.IS_QT6: + _ProcessEventFlagType = QEventLoop.ProcessEventsFlag +else: + _ProcessEventFlagType = QEventLoop.ProcessEventsFlags + + class EventLoop(QEventLoop): """A thin wrapper around QEventLoop. @@ -463,8 +475,9 @@ class EventLoop(QEventLoop): def exec( self, - flags: QEventLoop.ProcessEventsFlags = - cast(QEventLoop.ProcessEventsFlags, QEventLoop.AllEvents) + flags: _ProcessEventFlagType = ( + QEventLoop.ProcessEventsFlag.AllEvents # type: ignore[assignment] + ), ) -> int: """Override exec_ to raise an exception when re-running.""" if self._executing: @@ -503,7 +516,7 @@ def interpolate_color( start: QColor, end: QColor, percent: int, - colorspace: Optional[QColor.Spec] = QColor.Rgb + colorspace: Optional[QColor.Spec] = QColor.Spec.Rgb ) -> QColor: """Get an interpolated color value. @@ -528,17 +541,17 @@ def interpolate_color( return QColor(*start.getRgb()) out = QColor() - if colorspace == QColor.Rgb: + if colorspace == QColor.Spec.Rgb: r1, g1, b1, a1 = start.getRgb() r2, g2, b2, a2 = end.getRgb() components = _get_color_percentage(r1, g1, b1, a1, r2, g2, b2, a2, percent) out.setRgb(*components) - elif colorspace == QColor.Hsv: + elif colorspace == QColor.Spec.Hsv: h1, s1, v1, a1 = start.getHsv() h2, s2, v2, a2 = end.getHsv() components = _get_color_percentage(h1, s1, v1, a1, h2, s2, v2, a2, percent) out.setHsv(*components) - elif colorspace == QColor.Hsl: + elif colorspace == QColor.Spec.Hsl: h1, s1, l1, a1 = start.getHsl() h2, s2, l2, a2 = end.getHsl() components = _get_color_percentage(h1, s1, l1, a1, h2, s2, l2, a2, percent) @@ -548,3 +561,54 @@ def interpolate_color( out = out.convertTo(start.spec()) ensure_valid(out) return out + + +class LibraryPath(enum.Enum): + + """A path to be passed to QLibraryInfo. + + Should mirror QLibraryPath (Qt 5) and QLibraryLocation (Qt 6). + Values are the respective Qt names. + """ + + prefix = "PrefixPath" + documentation = "DocumentationPath" + headers = "HeadersPath" + libraries = "LibrariesPath" + library_executables = "LibraryExecutablesPath" + binaries = "BinariesPath" + plugins = "PluginsPath" + qml2_imports = "Qml2ImportsPath" + arch_data = "ArchDataPath" + data = "DataPath" + translations = "TranslationsPath" + examples = "ExamplesPath" + tests = "TestsPath" + settings = "SettingsPath" + + +def library_path(which: LibraryPath) -> pathlib.Path: + """Wrapper around QLibraryInfo.path / .location.""" + if machinery.IS_QT6: + val = getattr(QLibraryInfo.LibraryPath, which.value) + ret = QLibraryInfo.path(val) + else: + # Qt 5 + val = getattr(QLibraryInfo.LibraryLocation, which.value) + ret = QLibraryInfo.location(val) + assert ret + return pathlib.Path(ret) + + +def extract_enum_val(val: Union[sip.simplewrapper, int, enum.Enum]) -> int: + """Extract an int value from a Qt enum value. + + For Qt 5, enum values are basically Python integers. + For Qt 6, they are usually enum.Enum instances, with the value set to the + integer. + """ + if isinstance(val, enum.Enum): + return val.value + elif isinstance(val, sip.simplewrapper): + return int(val) # type: ignore[call-overload] + return val |