Index: README.md ================================================================== --- README.md +++ README.md @@ -88,11 +88,11 @@ These external libraries are used in the code. They can be installed from PyPI. - `platformdirs` - `ijson` - `pint` -- `PyQt5` +- `PySide6` - `delegateto` - `PyHamcrest` - `cx_Freeze` (Stand-alone bundles only. Used by the installer for Windows®-based platforms.) ### System libraries Index: build_MTGProxyPrinter_packages.bat ================================================================== --- build_MTGProxyPrinter_packages.bat +++ build_MTGProxyPrinter_packages.bat @@ -1,10 +1,10 @@ :: Generate an application bundle using cx_Freeze :: Create or activate the build environment -IF EXIST "venv" ( - call venv\Scripts\activate.bat +IF EXIST "venv-PySide6" ( + call venv-PySide6\Scripts\activate.bat ) ELSE ( call create_development_environment.bat ) :: Create a platform-dependent, portable build in the build directory Index: build_MTGProxyPrinter_packages.sh ================================================================== --- build_MTGProxyPrinter_packages.sh +++ build_MTGProxyPrinter_packages.sh @@ -1,7 +1,7 @@ #!/bin/bash -ENVIRONMENT_NAME="venv" +ENVIRONMENT_NAME="venv-PySide6" # Generate an application bundle using cx_Freeze for Linux. if [ ! -e "${ENVIRONMENT_NAME}" ]; then ./create_development_environment.sh fi Index: create_development_environment.bat ================================================================== --- create_development_environment.bat +++ create_development_environment.bat @@ -1,8 +1,8 @@ -python -m venv venv +python -m venv venv-PySide6 -call venv\Scripts\activate.bat +call venv-PySide6\Scripts\activate.bat python -m pip install --upgrade pip python -m pip install wheel python -m pip install "pip-tools >= 7" python -m piptools compile -o requirements.txt pyproject.toml Index: create_development_environment.sh ================================================================== --- create_development_environment.sh +++ create_development_environment.sh @@ -1,7 +1,7 @@ #!/bin/bash -ENVIRONMENT_NAME="venv" +ENVIRONMENT_NAME="venv-PySide6" if [ -e "${ENVIRONMENT_NAME}" ]; then echo "Removing already existing virtual environment." rm -r "${ENVIRONMENT_NAME}" fi Index: doc/ThirdPartyLicenses.md ================================================================== --- doc/ThirdPartyLicenses.md +++ doc/ThirdPartyLicenses.md @@ -509,19 +509,16 @@ permanent authorization for you to choose that version for the Library.   -# PyQt5 - -Copyright (c) [Riverbank Computing Limited](https://www.riverbankcomputing.com/). - -The included copy is licensed under the terms of the -GNU General Public License version 3 as published by the Free Software Foundation. - -See LICENSE.md (when viewing from the source code archive) or the License tab in the -MTGProxyPrinter About dialog for details. +# Qt6, PySide6 + +Qt and PySide6 are available under the GNU Lesser General Public License version 3. + +The Qt Toolkit is Copyright (C) 2018 The Qt Company Ltd. and other contributors. +Contact: https://www.qt.io/licensing/   # cx_Freeze Index: mtg_proxy_printer/__main__.py ================================================================== --- mtg_proxy_printer/__main__.py +++ mtg_proxy_printer/__main__.py @@ -18,12 +18,11 @@ import os import platform import sys -from PyQt5.QtCore import Qt, QTimer -from PyQt5.QtWidgets import QApplication +from PySide6.QtCore import QTimer import mtg_proxy_printer.app_dirs from mtg_proxy_printer.argument_parser import parse_args import mtg_proxy_printer.logger from mtg_proxy_printer.application import Application @@ -58,22 +57,19 @@ global _app arguments = parse_args() mtg_proxy_printer.app_dirs.migrate_from_old_appdirs() mtg_proxy_printer.logger.configure_root_logger() handle_ssl_certificates() - # According to https://doc.qt.io/qt-5/qt.html#ApplicationAttribute-enum, - # Qt.AA_EnableHighDpiScaling has to be set prior to creating the QApplication instance - QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) _app = Application(arguments, sys.argv) if arguments.test_exit_on_launch: logger.info("Skipping startup tasks, because immediate application exit was requested.") QTimer.singleShot(0, _app.main_window.on_action_quit_triggered) else: logger.debug("Enqueueing startup tasks.") _app.enqueue_startup_tasks(arguments) logger.debug("Initialisation done. Starting event loop.") - _app.exec_() + _app.exec() logger.debug("Left event loop.") if __name__ == "__main__": main() Index: mtg_proxy_printer/application.py ================================================================== --- mtg_proxy_printer/application.py +++ mtg_proxy_printer/application.py @@ -21,13 +21,13 @@ import shutil import sys from tempfile import mkdtemp import typing -from PyQt5.QtCore import pyqtSlot as Slot, Qt, QTimer, QStringListModel, QThreadPool, QTranslator, QLocale, QLibraryInfo -from PyQt5.QtWidgets import QApplication -from PyQt5.QtGui import QIcon +from PySide6.QtCore import Slot, QTimer, QStringListModel, QThreadPool, QTranslator, QLocale, QLibraryInfo +from PySide6.QtWidgets import QApplication +from PySide6.QtGui import QIcon from mtg_proxy_printer.argument_parser import Namespace from mtg_proxy_printer import meta_data import mtg_proxy_printer.model.carddb import mtg_proxy_printer.carddb_migrations @@ -56,17 +56,18 @@ def __init__(self, args: Namespace, argv: typing.List[str] = None): if argv is None: argv = sys.argv logger.info(f"Starting MTGProxyPrinter version {meta_data.__version__}") - if not os.getenv("QT_QPA_PLUGIN") and "-platform" not in argv and platform.system() == "Windows": - logger.info("Running on Windows without explicit platform override. Enabling dark mode rendering.") - # The explicit set platform and parameters overwrite the environment, so set these options iff neither - # present as parameters nor environment variables. - argv.append("-platform") - argv.append("windows:darkmode=1") super().__init__(argv) + # Note: The sub-expression '"-style" not in argv' breaks, if "-style" is passed as a value for another + # argument or as a positional argument. + # (For example, if the user wants to load a document with file name "-style" via command line argument.) + if platform.system() == "Windows" and "-style" not in argv and not os.getenv("QT_STYLE_OVERRIDE"): + logger.info("Running on Windows without explicit style set. Use 'fusion', which supports dark mode.") + # Set a dark-mode compatible style, if on Windows and the user does not override the style. + self.setStyle("fusion") # Used by the with_database_write_lock decorator to not start un-frozen, # waiting tasks when the application is about to exit self.should_run = True self.args = args self._setup_translations() @@ -219,11 +220,11 @@ f"Possible display languages are: {locale.uiLanguages()}") path = ":" if mtg_proxy_printer.ui.common.HAS_COMPILED_RESOURCES \ else str(pathlib.Path(mtg_proxy_printer.__file__).parent / "resources") path += "/translations" logger.debug(f"Locale search path is '{path}'") - self._load_translator(locale, "qtbase", QLibraryInfo.location(QLibraryInfo.LibraryLocation.TranslationsPath)) + self._load_translator(locale, "qtbase", QLibraryInfo.location(QLibraryInfo.LibraryPath.TranslationsPath)) self._load_translator(locale, "mtgproxyprinter", path) def _load_translator(self, locale: QLocale, component: str, path: str): translator = QTranslator(self) if translator.load(locale, component, '_', path): @@ -256,12 +257,10 @@ QIcon.setThemeSearchPaths(theme_search_paths) QIcon.setThemeName("breeze") else: logger.debug(f"Using system-provided icon theme '{fallback_icon_theme}'") - self.setAttribute(Qt.AA_UseHighDpiPixmaps) - @Slot() def quit(self): logger.info("About to exit.") self.should_run = False self.main_window.hide() Index: mtg_proxy_printer/card_info_downloader.py ================================================================== --- mtg_proxy_printer/card_info_downloader.py +++ mtg_proxy_printer/card_info_downloader.py @@ -26,11 +26,11 @@ import urllib.error import urllib.parse import urllib.request import ijson -from PyQt5.QtCore import pyqtSignal as Signal, QObject, Qt, QThreadPool +from PySide6.QtCore import Signal, QObject, Qt, QThreadPool from mtg_proxy_printer.downloader_base import DownloaderBase from mtg_proxy_printer.model.carddb import CardDatabase, SCHEMA_NAME, with_database_write_lock from mtg_proxy_printer.sqlite_helpers import cached_dedent from mtg_proxy_printer.printing_filter_updater import PrintingFilterUpdater Index: mtg_proxy_printer/carddb_migrations.py ================================================================== --- mtg_proxy_printer/carddb_migrations.py +++ mtg_proxy_printer/carddb_migrations.py @@ -30,11 +30,11 @@ import urllib.error import urllib.parse from textwrap import dedent from typing import List, Dict, Union, Tuple, Any, Generator, Callable -from PyQt5.QtCore import QCoreApplication, Qt +from PySide6.QtCore import QCoreApplication, Qt try: from typing import LiteralString except AttributeError: from typing_extensions import LiteralString Index: mtg_proxy_printer/decklist_downloader.py ================================================================== --- mtg_proxy_printer/decklist_downloader.py +++ mtg_proxy_printer/decklist_downloader.py @@ -26,11 +26,11 @@ import platform import re import typing import ijson -from PyQt5.QtGui import QValidator +from PySide6.QtGui import QValidator from mtg_proxy_printer.downloader_base import DownloaderBase from mtg_proxy_printer.decklist_parser.common import ParserBase from mtg_proxy_printer.decklist_parser.csv_parsers import ScryfallCSVParser, TappedOutCSVParser from mtg_proxy_printer.decklist_parser.re_parsers import MTGArenaParser, MagicWorkstationDeckDataFormatParser, \ Index: mtg_proxy_printer/decklist_parser/common.py ================================================================== --- mtg_proxy_printer/decklist_parser/common.py +++ mtg_proxy_printer/decklist_parser/common.py @@ -14,11 +14,11 @@ # along with this program. If not, see . from abc import abstractmethod import typing -from PyQt5.QtCore import QObject, pyqtSignal as Signal +from PySide6.QtCore import QObject, Signal from mtg_proxy_printer.model.carddb import Card, CardDatabase, CardIdentificationData from mtg_proxy_printer.model.imagedb import ImageDatabase import mtg_proxy_printer.settings from mtg_proxy_printer.logger import get_logger Index: mtg_proxy_printer/decklist_parser/csv_parsers.py ================================================================== --- mtg_proxy_printer/decklist_parser/csv_parsers.py +++ mtg_proxy_printer/decklist_parser/csv_parsers.py @@ -16,11 +16,11 @@ import abc import collections import csv import typing -from PyQt5.QtCore import QObject, QCoreApplication +from PySide6.QtCore import QObject, QCoreApplication from mtg_proxy_printer.model.carddb import Card, CardDatabase, CardIdentificationData from mtg_proxy_printer.model.imagedb import ImageDatabase from .common import ParsedDeck, ParserBase Index: mtg_proxy_printer/decklist_parser/re_parsers.py ================================================================== --- mtg_proxy_printer/decklist_parser/re_parsers.py +++ mtg_proxy_printer/decklist_parser/re_parsers.py @@ -15,11 +15,11 @@ import copy from collections import Counter import re import typing -from PyQt5.QtCore import QObject, QCoreApplication +from PySide6.QtCore import QObject, QCoreApplication from mtg_proxy_printer.decklist_parser.common import ParsedDeck, ParserBase from mtg_proxy_printer.model.carddb import Card, CardDatabase, CardIdentificationData from mtg_proxy_printer.model.imagedb import ImageDatabase from mtg_proxy_printer.logger import get_logger @@ -172,13 +172,14 @@ class MagicWorkstationDeckDataFormatParser(GenericRegularExpressionDeckParser): @staticmethod def supported_file_types() -> typing.Dict[str, typing.List[str]]: return { - QCoreApplication.translate( - "MagicWorkstationDeckDataFormatParser", "Magic Workstation Deck Data Format"): ["mwDeck"], - } + QCoreApplication.translate( + "MagicWorkstationDeckDataFormatParser", "Magic Workstation Deck Data Format"): ["mwDeck"], + } + PREFIXES_TO_SKIP = frozenset({"//"}) def __init__(self, card_db: CardDatabase, image_db: ImageDatabase, parent: QObject = None): super().__init__( card_db, image_db, @@ -251,13 +252,14 @@ @staticmethod def supported_file_types() -> typing.Dict[str, typing.List[str]]: return { QCoreApplication.translate("XMageParser", "XMage Deck file"): ["dck"], - } + } + PREFIXES_TO_SKIP = frozenset(("NAME", "LAYOUT")) def __init__(self, card_db: CardDatabase, image_db: ImageDatabase, parent: QObject = None): super().__init__( card_db, image_db, re.compile(r"(SB: )?(?P\d+) \[(?P\w+):(?P[^]]+)] (?P.+)"), parent ) Index: mtg_proxy_printer/document_controller/_interface.py ================================================================== --- mtg_proxy_printer/document_controller/_interface.py +++ mtg_proxy_printer/document_controller/_interface.py @@ -17,11 +17,11 @@ from functools import partial import itertools import operator import typing -from PyQt5.QtCore import QCoreApplication +from PySide6.QtCore import QCoreApplication from mtg_proxy_printer.units_and_sizes import StringList if typing.TYPE_CHECKING: from mtg_proxy_printer.model.document import Document Index: mtg_proxy_printer/document_controller/move_cards.py ================================================================== --- mtg_proxy_printer/document_controller/move_cards.py +++ mtg_proxy_printer/document_controller/move_cards.py @@ -15,11 +15,11 @@ import functools import itertools import typing -from PyQt5.QtCore import QModelIndex +from PySide6.QtCore import QModelIndex from ._interface import DocumentAction, IllegalStateError, Self from mtg_proxy_printer.logger import get_logger if typing.TYPE_CHECKING: Index: mtg_proxy_printer/document_controller/replace_card.py ================================================================== --- mtg_proxy_printer/document_controller/replace_card.py +++ mtg_proxy_printer/document_controller/replace_card.py @@ -14,11 +14,11 @@ # along with this program. If not, see . import functools import typing -from PyQt5.QtCore import Qt +from PySide6.QtCore import Qt from mtg_proxy_printer.model.carddb import Card from mtg_proxy_printer.model.card_list import PageColumns if typing.TYPE_CHECKING: from mtg_proxy_printer.model.document_page import CardContainer Index: mtg_proxy_printer/document_controller/shuffle_document.py ================================================================== --- mtg_proxy_printer/document_controller/shuffle_document.py +++ mtg_proxy_printer/document_controller/shuffle_document.py @@ -20,11 +20,11 @@ # Compatibility with Py 3.8 from secrets import token_bytes as randbytes import typing -from PyQt5.QtCore import Qt, QModelIndex +from PySide6.QtCore import Qt, QModelIndex from ._interface import DocumentAction, IllegalStateError, Self from mtg_proxy_printer.model.carddb import Card from mtg_proxy_printer.model.card_list import PageColumns from mtg_proxy_printer.model.document_page import CardContainer Index: mtg_proxy_printer/downloader_base.py ================================================================== --- mtg_proxy_printer/downloader_base.py +++ mtg_proxy_printer/downloader_base.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import gzip -from PyQt5.QtCore import QObject, pyqtSignal as Signal +from PySide6.QtCore import QObject, Signal import mtg_proxy_printer.http_file from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) del get_logger Index: mtg_proxy_printer/http_file.py ================================================================== --- mtg_proxy_printer/http_file.py +++ mtg_proxy_printer/http_file.py @@ -24,11 +24,11 @@ import typing from typing import List, Optional, Dict import urllib.error import urllib.request -from PyQt5.QtCore import QObject, pyqtSignal as Signal +from PySide6.QtCore import QObject, Signal import delegateto from mtg_proxy_printer.meta_data import USER_AGENT from mtg_proxy_printer.logger import get_logger Index: mtg_proxy_printer/meta_data.py ================================================================== --- mtg_proxy_printer/meta_data.py +++ mtg_proxy_printer/meta_data.py @@ -12,11 +12,11 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAMNAME = "MTGProxyPrinter" -__version__ = "0.29.1" +__version__ = "0.29.1+PySide6" COPYRIGHT = "(C) 2020-2024 Thomas Hess" HOME_PAGE = "https://chiselapp.com/user/luziferius/repository/MTGProxyPrinter" DOWNLOAD_WEB_PAGE = f"{HOME_PAGE}/uv/download.html" USER_AGENT = f"{PROGRAMNAME}/{__version__} ({HOME_PAGE})" Index: mtg_proxy_printer/metered_file.py ================================================================== --- mtg_proxy_printer/metered_file.py +++ mtg_proxy_printer/metered_file.py @@ -15,11 +15,11 @@ from typing import Iterable, List, Optional, BinaryIO, Union from io import BufferedIOBase -from PyQt5.QtCore import QObject, pyqtSignal as Signal +from PySide6.QtCore import QObject, Signal from delegateto import delegate from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) Index: mtg_proxy_printer/missing_images_manager.py ================================================================== --- mtg_proxy_printer/missing_images_manager.py +++ mtg_proxy_printer/missing_images_manager.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import typing -from PyQt5.QtCore import QObject, pyqtSignal as Signal, pyqtSlot as Slot +from PySide6.QtCore import QObject, Signal, Slot from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) del get_logger Index: mtg_proxy_printer/model/card_list.py ================================================================== --- mtg_proxy_printer/model/card_list.py +++ mtg_proxy_printer/model/card_list.py @@ -15,12 +15,12 @@ import enum import itertools import typing -from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt, pyqtSlot as Slot, pyqtSignal as Signal, QItemSelection -from PyQt5.QtGui import QIcon +from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot, Signal, QItemSelection +from PySide6.QtGui import QIcon from mtg_proxy_printer.model.carddb import Card, CardIdentificationData, CardDatabase, AnyCardType from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) @@ -97,11 +97,11 @@ if role == ItemDataRole.ToolTipRole: return self.tr("Beware: Potentially oversized card!\nThis card may not fit in your deck.") elif role == ItemDataRole.DecorationRole: return self._oversized_icon - def flags(self, index: QModelIndex) -> Qt.ItemFlags: + def flags(self, index: QModelIndex) -> ItemFlag: flags = super().flags(index) if index.column() in self.EDITABLE_COLUMNS: flags |= ItemFlag.ItemIsEditable return flags Index: mtg_proxy_printer/model/carddb.py ================================================================== --- mtg_proxy_printer/model/carddb.py +++ mtg_proxy_printer/model/carddb.py @@ -22,13 +22,13 @@ import pathlib import sqlite3 import threading import typing -from PyQt5.QtWidgets import QApplication -from PyQt5.QtGui import QPixmap, QColor, QTransform, QPainter, QColorConstants -from PyQt5.QtCore import Qt, QPoint, QRect, QSize, QPointF, QObject, pyqtSignal as Signal, pyqtSlot as Slot +from PySide6.QtWidgets import QApplication +from PySide6.QtGui import QPixmap, QColor, QTransform, QPainter, QColorConstants +from PySide6.QtCore import Qt, QPoint, QRect, QSize, QPointF, QObject, Signal, Slot if typing.TYPE_CHECKING: from mtg_proxy_printer.model.imagedb import CacheContent import mtg_proxy_printer.app_dirs @@ -150,11 +150,11 @@ round(self.image_file.width() * corner.value[0]), round(self.image_file.height() * corner.value[1])), QSize(10, 10) )) average_color = sample_area.scaled( - 1, 1, transformMode=Qt.TransformationMode.SmoothTransformation).toImage().pixelColor(0, 0) + 1, 1, mode=Qt.TransformationMode.SmoothTransformation).toImage().pixelColor(0, 0) return average_color def display_string(self): return f'"{self.name}" [{self.set.code.upper()}:{self.collector_number}]' @@ -227,11 +227,11 @@ vertical_scaling_factor = card_size.width() / card_size.height() horizontal_scaling_factor = card_size.height()/(card_size.width()*2) combined_image = QPixmap(card_size) combined_image.fill(QColor.fromRgb(255, 255, 255, 0)) # Fill with fully transparent white painter = QPainter(combined_image) - painter.setRenderHints(RenderHint.SmoothPixmapTransform | RenderHint.HighQualityAntialiasing) + painter.setRenderHints(RenderHint.SmoothPixmapTransform | RenderHint.Antialiasing) transformation = QTransform() transformation.rotate(90) transformation.scale(horizontal_scaling_factor, vertical_scaling_factor) painter.setTransform(transformation) painter.drawPixmap(QPointF(card_size.width(), -card_size.height()), self.back.image_file) Index: mtg_proxy_printer/model/document.py ================================================================== --- mtg_proxy_printer/model/document.py +++ mtg_proxy_printer/model/document.py @@ -21,11 +21,11 @@ import pathlib import sys import textwrap import typing -from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt, pyqtSlot as Slot, pyqtSignal as Signal, \ +from PySide6.QtCore import QAbstractItemModel, QModelIndex, Qt, Slot, Signal, \ QPersistentModelIndex import mtg_proxy_printer.sqlite_helpers from mtg_proxy_printer.model.document_page import CardContainer, Page, PageList from mtg_proxy_printer.units_and_sizes import PageType Index: mtg_proxy_printer/model/document_loader.py ================================================================== --- mtg_proxy_printer/model/document_loader.py +++ mtg_proxy_printer/model/document_loader.py @@ -24,12 +24,12 @@ import textwrap import typing from unittest.mock import patch import pint -from PyQt5.QtGui import QPageLayout, QPageSize -from PyQt5.QtCore import QObject, pyqtSignal as Signal, QThreadPool, QMarginsF, QSizeF, Qt +from PySide6.QtGui import QPageLayout, QPageSize +from PySide6.QtCore import QObject, Signal, QThreadPool, QMarginsF, QSizeF, Qt from hamcrest import assert_that, all_of, instance_of, greater_than_or_equal_to, matches_regexp, is_in, \ has_properties, is_, any_of try: from hamcrest import contains_exactly Index: mtg_proxy_printer/model/imagedb.py ================================================================== --- mtg_proxy_printer/model/imagedb.py +++ mtg_proxy_printer/model/imagedb.py @@ -24,12 +24,12 @@ import string import threading import typing import urllib.error -from PyQt5.QtCore import QObject, pyqtSignal as Signal, pyqtSlot as Slot, QModelIndex, Qt, QThreadPool -from PyQt5.QtGui import QPixmap, QColorConstants +from PySide6.QtCore import QObject, Signal, Slot, QModelIndex, Qt, QThreadPool +from PySide6.QtGui import QPixmap, QColorConstants if typing.TYPE_CHECKING: from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.model.carddb import with_database_write_lock Index: mtg_proxy_printer/model/string_list.py ================================================================== --- mtg_proxy_printer/model/string_list.py +++ mtg_proxy_printer/model/string_list.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import typing -from PyQt5.QtCore import QAbstractListModel, Qt, QObject, QModelIndex +from PySide6.QtCore import QAbstractListModel, Qt, QObject, QModelIndex from mtg_proxy_printer.model.carddb import MTGSet __all__ = [ Index: mtg_proxy_printer/natsort.py ================================================================== --- mtg_proxy_printer/natsort.py +++ mtg_proxy_printer/natsort.py @@ -18,11 +18,11 @@ """ import re import typing -from PyQt5.QtCore import QSortFilterProxyModel, QModelIndex +from PySide6.QtCore import QSortFilterProxyModel, QModelIndex __all__ = [ "natural_sorted", "str_less_than", "NaturallySortedSortFilterProxyModel", Index: mtg_proxy_printer/print.py ================================================================== --- mtg_proxy_printer/print.py +++ mtg_proxy_printer/print.py @@ -14,13 +14,13 @@ # along with this program. If not, see . import math from pathlib import Path -from PyQt5.QtCore import QObject, QMarginsF, QSizeF, pyqtSlot as Slot, QPersistentModelIndex -from PyQt5.QtGui import QPainter, QPdfWriter, QPageSize -from PyQt5.QtPrintSupport import QPrinter +from PySide6.QtCore import QObject, QMarginsF, QSizeF, Slot, QPersistentModelIndex +from PySide6.QtGui import QPainter, QPdfWriter, QPageSize +from PySide6.QtPrintSupport import QPrinter import mtg_proxy_printer.meta_data from mtg_proxy_printer.settings import settings from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.model.document_loader import PageLayoutSettings @@ -61,11 +61,10 @@ f"orientation={page_layout.orientation()}, " f"margins={layout.margin_left, layout.margin_top, layout.margin_right, layout.margin_bottom}") # magnitude returns a float by default, so round to int to avoid a TypeError printer.setResolution(round(mtg_proxy_printer.units_and_sizes.RESOLUTION.magnitude)) # Disable duplex printing by default - printer.setDoubleSidedPrinting(False) printer.setDuplex(QPrinter.DuplexMode.DuplexNone) printer.setOutputFormat(QPrinter.OutputFormat.NativeFormat) if RenderMode.IMPLICIT_MARGINS not in renderer.render_mode: printer.setFullPage(True) return printer Index: mtg_proxy_printer/printing_filter_updater.py ================================================================== --- mtg_proxy_printer/printing_filter_updater.py +++ mtg_proxy_printer/printing_filter_updater.py @@ -14,11 +14,11 @@ # along with this program. If not, see . import sqlite3 import typing -from PyQt5.QtCore import QObject, pyqtSignal as Signal, Qt, QCoreApplication +from PySide6.QtCore import QObject, Signal, Qt, QCoreApplication import mtg_proxy_printer.settings if typing.TYPE_CHECKING: from mtg_proxy_printer.model.carddb import CardDatabase from mtg_proxy_printer.ui.main_window import MainWindow Index: mtg_proxy_printer/resources/ui/central_widget/columnar.ui ================================================================== --- mtg_proxy_printer/resources/ui/central_widget/columnar.ui +++ mtg_proxy_printer/resources/ui/central_widget/columnar.ui @@ -24,11 +24,11 @@ false - QPainter::Antialiasing|QPainter::HighQualityAntialiasing + QPainter::Antialiasing Index: mtg_proxy_printer/resources/ui/central_widget/grouped.ui ================================================================== --- mtg_proxy_printer/resources/ui/central_widget/grouped.ui +++ mtg_proxy_printer/resources/ui/central_widget/grouped.ui @@ -24,11 +24,11 @@ false - QPainter::Antialiasing|QPainter::HighQualityAntialiasing + QPainter::Antialiasing Index: mtg_proxy_printer/resources/ui/document_settings_dialog.ui ================================================================== --- mtg_proxy_printer/resources/ui/document_settings_dialog.ui +++ mtg_proxy_printer/resources/ui/document_settings_dialog.ui @@ -1,17 +1,9 @@ DocumentSettingsDialog - - - 0 - 0 - 501 - 300 - - Set Document settings @@ -42,34 +34,14 @@ button_box accepted() DocumentSettingsDialog accept() - - - 248 - 254 - - - 157 - 274 - - button_box rejected() DocumentSettingsDialog reject() - - - 316 - 260 - - - 286 - 274 - - Index: mtg_proxy_printer/runner.py ================================================================== --- mtg_proxy_printer/runner.py +++ mtg_proxy_printer/runner.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import typing -from PyQt5.QtCore import QRunnable, QObject, pyqtSignal as Signal +from PySide6.QtCore import QRunnable, QObject, Signal from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) del get_logger Index: mtg_proxy_printer/settings.py ================================================================== --- mtg_proxy_printer/settings.py +++ mtg_proxy_printer/settings.py @@ -19,11 +19,11 @@ import re import typing import tokenize import pint -from PyQt5.QtCore import QStandardPaths +from PySide6.QtCore import QStandardPaths import mtg_proxy_printer.app_dirs import mtg_proxy_printer.meta_data import mtg_proxy_printer.natsort from mtg_proxy_printer.units_and_sizes import CardSizes, ConfigParser, SectionProxy, unit_registry, T, QuantityT, UnitT Index: mtg_proxy_printer/ui/add_card.py ================================================================== --- mtg_proxy_printer/ui/add_card.py +++ mtg_proxy_printer/ui/add_card.py @@ -13,13 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from typing import Union, Type, Optional -from PyQt5.QtCore import QStringListModel, pyqtSlot as Slot, pyqtSignal as Signal, Qt, QItemSelectionModel, QItemSelection -from PyQt5.QtWidgets import QWidget, QDialogButtonBox -from PyQt5.QtGui import QIcon +from PySide6.QtCore import QStringListModel, Slot, Signal, Qt, QItemSelectionModel, QItemSelection +from PySide6.QtWidgets import QWidget, QDialogButtonBox +from PySide6.QtGui import QIcon from mtg_proxy_printer.document_controller.card_actions import ActionAddCard import mtg_proxy_printer.model.string_list import mtg_proxy_printer.model.carddb import mtg_proxy_printer.model.document Index: mtg_proxy_printer/ui/cache_cleanup_wizard.py ================================================================== --- mtg_proxy_printer/ui/cache_cleanup_wizard.py +++ mtg_proxy_printer/ui/cache_cleanup_wizard.py @@ -19,13 +19,13 @@ import functools import math import pathlib import typing -from PyQt5.QtCore import QAbstractTableModel, Qt, QModelIndex, QObject, QBuffer, QIODevice, QItemSelectionModel, QSize -from PyQt5.QtGui import QIcon, QPixmap -from PyQt5.QtWidgets import QWidget, QWizard, QWizardPage +from PySide6.QtCore import QAbstractTableModel, Qt, QModelIndex, QObject, QBuffer, QIODevice, QItemSelectionModel, QSize +from PySide6.QtGui import QIcon, QPixmap +from PySide6.QtWidgets import QWidget, QWizard, QWizardPage import mtg_proxy_printer.settings from mtg_proxy_printer.natsort import NaturallySortedSortFilterProxyModel from mtg_proxy_printer.model.carddb import CardDatabase, Card, MTGSet from mtg_proxy_printer.model.imagedb import ImageDatabase, CacheContent as ImageCacheContent, ImageKey @@ -61,11 +61,11 @@ source.width() // scaling_factor, source.height() // scaling_factor, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) buffer = QBuffer() buffer.open(QIODevice.OpenModeFlag.WriteOnly) pixmap.save(buffer, "PNG", quality=100) - image = buffer.data().toBase64().data().decode() + image = buffer.data().toBase64().toStdString() card_name = f'

{card_name}


' if card_name else "" tooltip_text = f'{card_name}' return tooltip_text Index: mtg_proxy_printer/ui/central_widget.py ================================================================== --- mtg_proxy_printer/ui/central_widget.py +++ mtg_proxy_printer/ui/central_widget.py @@ -17,14 +17,14 @@ import math import operator import pathlib from typing import Union, Type, Optional -from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot, QPersistentModelIndex, QItemSelectionModel, \ +from PySide6.QtCore import Signal, Slot, QPersistentModelIndex, QItemSelectionModel, \ QModelIndex, QPoint, Qt -from PyQt5.QtGui import QIcon -from PyQt5.QtWidgets import QWidget, QAction, QMenu, QInputDialog, QFileDialog +from PySide6.QtGui import QIcon, QAction +from PySide6.QtWidgets import QWidget, QMenu, QInputDialog, QFileDialog import mtg_proxy_printer.app_dirs import mtg_proxy_printer.settings from mtg_proxy_printer.model.card_list import PageColumns from mtg_proxy_printer.model.document import Document Index: mtg_proxy_printer/ui/common.py ================================================================== --- mtg_proxy_printer/ui/common.py +++ mtg_proxy_printer/ui/common.py @@ -15,15 +15,14 @@ import pathlib import platform import typing -from PyQt5.QtCore import QFile, QUrl, QObject, QSize, QCoreApplication -from PyQt5.QtWidgets import QLabel, QWizard, QWidget, QGraphicsColorizeEffect, QTextEdit -from PyQt5.QtGui import QIcon -# noinspection PyUnresolvedReferences -from PyQt5 import uic +from PySide6.QtCore import QFile, QUrl, QObject, QSize, QCoreApplication +from PySide6.QtWidgets import QLabel, QWizard, QWidget, QGraphicsColorizeEffect, QTextEdit +from PySide6.QtGui import QIcon +from PySide6.QtUiTools import loadUiType from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) del get_logger @@ -92,22 +91,24 @@ label.setText(f"""{display_text:s}""") def load_ui_from_file(name: str): """ - Returns the Ui class type from uic.loadUiType(), loading the ui file with the given name. - + Returns the Ui class type as returned by PySide6.QtUiTools.loadUiType(), loading the ui file with the given name. :param name: Path to the UI file :return: class implementing the requested Ui :raises FileNotFoundError: If the given ui file does not exist """ file_path = f"{RESOURCE_PATH_PREFIX}/ui/{name}.ui" if not QFile.exists(file_path): error_message = f"UI file not found: {file_path}" logger.error(error_message) raise FileNotFoundError(error_message) - base_type, _ = uic.loadUiType(file_path, from_imports=True) + try: + base_type, _ = loadUiType(file_path) + except TypeError as e: + raise RuntimeError(f"Ui compilation failed for path {file_path}") from e return base_type def load_icon(name: str) -> QIcon: file_path = f"{RESOURCE_PATH_PREFIX}/icons/{name}" if not QFile.exists(file_path): Index: mtg_proxy_printer/ui/compiled_resources.pyi ================================================================== --- mtg_proxy_printer/ui/compiled_resources.pyi +++ mtg_proxy_printer/ui/compiled_resources.pyi @@ -12,11 +12,11 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ -Declares the interface created by the PyQt5 resource compiler. +Declares the interface created by the PySide6 resource compiler. This is only used for type hinting. """ import typing Index: mtg_proxy_printer/ui/deck_import_wizard.py ================================================================== --- mtg_proxy_printer/ui/deck_import_wizard.py +++ mtg_proxy_printer/ui/deck_import_wizard.py @@ -20,14 +20,15 @@ import re import typing import urllib.error import urllib.parse -from PyQt5.QtCore import pyqtSlot as Slot, pyqtSignal as Signal, pyqtProperty as Property, QStringListModel, Qt, \ +from PySide6.QtCore import Slot, Signal, Property, QStringListModel, Qt, SIGNAL, \ QItemSelection, QSize, QUrl -from PyQt5.QtGui import QValidator, QIcon, QDesktopServices -from PyQt5.QtWidgets import QWizard, QFileDialog, QMessageBox, QWizardPage, QWidget, QRadioButton +from PySide6.QtGui import QValidator, QIcon, QDesktopServices +from PySide6.QtWidgets import QWizard, QFileDialog, QMessageBox, QWizardPage, QWidget, QRadioButton + from mtg_proxy_printer.units_and_sizes import SectionProxy import mtg_proxy_printer.settings from mtg_proxy_printer.decklist_parser import re_parsers, common, csv_parsers from mtg_proxy_printer.decklist_downloader import IsIdentifyingDeckUrlValidator, AVAILABLE_DOWNLOADERS, \ @@ -118,18 +119,18 @@ self.deck_list_url_validator.validate(text)[0] == State.Acceptable)) supported_sites = "\n".join((downloader.APPLICABLE_WEBSITES for downloader in AVAILABLE_DOWNLOADERS.values())) ui.deck_list_download_url_line_edit.setToolTip( self.tr("Supported websites:\n{supported_sites}").format(supported_sites=supported_sites)) ui.translate_deck_list_target_language.setModel(language_model) - self.registerField("deck_list*", ui.deck_list, "plainText", ui.deck_list.textChanged) + self.registerField("deck_list*", ui.deck_list) self.registerField("print-guessing-enable", ui.print_guessing_enable) self.registerField("print-guessing-prefer-already-downloaded", ui.print_guessing_prefer_already_downloaded) self.registerField("translate-deck-list-enable", ui.translate_deck_list_enable) - self.registerField("deck-list-downloaded", self, "deck_list_downloader", self.deck_list_downloader_changed) + self.registerField("deck-list-downloaded", self, "deck_list_downloader", "deck_list_downloader_changed(str)") self.registerField( "translate-deck-list-target-language", ui.translate_deck_list_target_language, - "currentText", ui.translate_deck_list_target_language.currentTextChanged + "currentText", "currentTextChanged(str)" ) logger.info(f"Created {self.__class__.__name__} instance.") @Property(str, notify=deck_list_downloader_changed) @@ -605,10 +606,11 @@ } def __init__(self, card_db: CardDatabase, image_db: ImageDatabase, language_model: QStringListModel, parent: QWidget = None, flags=Qt.WindowFlags()): super().__init__(QSize(1000, 600), parent, flags) + self.setDefaultProperty("QPlainTextEdit", "plainText", SIGNAL("textChanged()")) self.card_db = card_db self.select_deck_parser_page = SelectDeckParserPage(card_db, image_db, self) self.load_list_page = LoadListPage(language_model, self) self.summary_page = SummaryPage(card_db, self) self.addPage(self.load_list_page) Index: mtg_proxy_printer/ui/dialogs.py ================================================================== --- mtg_proxy_printer/ui/dialogs.py +++ mtg_proxy_printer/ui/dialogs.py @@ -15,14 +15,14 @@ import typing import pathlib import sys -from PyQt5.QtCore import QFile, pyqtSlot as Slot, QThreadPool, QObject, QEvent, Qt -from PyQt5.QtWidgets import QFileDialog, QWidget, QTextBrowser, QDialogButtonBox, QDialog -from PyQt5.QtGui import QIcon -from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrintDialog, QPrinter +from PySide6.QtCore import QFile, Slot, QThreadPool, QObject, QEvent, Qt +from PySide6.QtWidgets import QFileDialog, QWidget, QTextBrowser, QDialogButtonBox, QDialog +from PySide6.QtGui import QIcon +from PySide6.QtPrintSupport import QPrintPreviewDialog, QPrintDialog, QPrinter import mtg_proxy_printer.app_dirs from mtg_proxy_printer.model.carddb import Card, CardDatabase import mtg_proxy_printer.model.document import mtg_proxy_printer.model.imagedb @@ -247,11 +247,11 @@ def _set_text_browser_with_markdown_file_content(self, file_path: str, text_browser: QTextBrowser): file = QFile(file_path, self) file.open(QFile.OpenModeFlag.ReadOnly) try: - content = file.readAll().data().decode("utf-8") + content = file.readAll().toStdString() finally: file.close() text_browser.setMarkdown(content) @@ -367,7 +367,7 @@ self.document.apply(action) logger.debug("Saving settings in the document done.") def clear_highlight(self): """Clears all GUI widget highlights.""" - for item in self.findChildren((QWidget,), options=Qt.FindChildOption.FindChildrenRecursively): # type: QWidget + for item in self.findChildren(QWidget, options=Qt.FindChildOption.FindChildrenRecursively): # type: QWidget item.setGraphicsEffect(None) Index: mtg_proxy_printer/ui/item_delegates.py ================================================================== --- mtg_proxy_printer/ui/item_delegates.py +++ mtg_proxy_printer/ui/item_delegates.py @@ -12,12 +12,12 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import typing -from PyQt5.QtCore import QModelIndex, Qt, QAbstractItemModel, QSortFilterProxyModel -from PyQt5.QtWidgets import QStyledItemDelegate, QWidget, QStyleOptionViewItem, QComboBox +from PySide6.QtCore import QModelIndex, Qt, QAbstractItemModel, QSortFilterProxyModel +from PySide6.QtWidgets import QStyledItemDelegate, QWidget, QStyleOptionViewItem, QComboBox from mtg_proxy_printer.model.carddb import Card from mtg_proxy_printer.model.card_list import PageColumns from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.logger import get_logger Index: mtg_proxy_printer/ui/main_window.py ================================================================== --- mtg_proxy_printer/ui/main_window.py +++ mtg_proxy_printer/ui/main_window.py @@ -14,14 +14,15 @@ # along with this program. If not, see . import pathlib import typing -from PyQt5.QtCore import pyqtSlot as Slot, pyqtSignal as Signal, QStringListModel, QUrl, Qt, QSize -from PyQt5.QtGui import QCloseEvent, QKeySequence, QDesktopServices, QDragEnterEvent, QDropEvent, QPixmap -from PyQt5.QtWidgets import QApplication, QMessageBox, QAction, QWidget, QMainWindow, QDialog +from PySide6.QtCore import Slot, Signal, QStringListModel, QUrl, Qt, QSize +from PySide6.QtGui import QCloseEvent, QKeySequence, QAction, QDesktopServices, QDragEnterEvent, QDropEvent, QPixmap +from PySide6.QtWidgets import QApplication, QMessageBox, QWidget, QMainWindow, QDialog +from PySide6.QtPrintSupport import QPrintDialog from mtg_proxy_printer.missing_images_manager import MissingImagesManager from mtg_proxy_printer.card_info_downloader import CardInfoDownloader from mtg_proxy_printer.model.carddb import CardDatabase, Card, MTGSet from mtg_proxy_printer.model.imagedb import ImageDatabase @@ -123,11 +124,10 @@ ] for action, shortcut in actions_with_shortcuts: action.setShortcut(shortcut) def _setup_central_widget(self): - self.setCentralWidget(self.ui.central_widget) self.ui.central_widget.set_data(self.document, self.card_database, self.image_db) def _setup_loading_state_connections(self): for widget_or_action in self._get_widgets_and_actions_disabled_in_loading_state(): self.loading_state_changed.connect(widget_or_action.setDisabled) @@ -264,11 +264,12 @@ "This is passed as the {action} when asking the user about compacting the document if that can save pages") if self._ask_user_about_compacting_document(action_str) == StandardButton.Cancel: return self.current_dialog = PrintDialog(self.document, self) self.current_dialog.finished.connect(self.on_dialog_finished) - self.missing_images_manager.obtain_missing_images(self.current_dialog.open) + # Use the QDialog base class open() method, because QPrintDialog.open() performs additional, unwanted actions. + self.missing_images_manager.obtain_missing_images(super(QPrintDialog, self.current_dialog).open) @Slot() def on_action_print_preview_triggered(self): logger.info(f"User views the print preview.") action_str = self.tr( @@ -332,11 +333,11 @@ "This program requires downloading additional card data from Scryfall to operate the card search.\n" "Download the required data from Scryfall now?\n" "Without the data, you can only print custom cards by drag&dropping " "the image files onto the main window."), StandardButton.Yes | StandardButton.No, StandardButton.Yes) == StandardButton.Yes: - self.ui.action_download_card_data.trigger() + self.card_data_downloader.import_from_api() @Slot() def on_action_save_document_triggered(self): logger.debug("User clicked on Save") if self.document.save_file_path is None: @@ -427,11 +428,11 @@ "There are %n new printings available on Scryfall. Update the local data now?", "", estimated_card_count), StandardButton.Yes | StandardButton.No, StandardButton.Yes ) == StandardButton.Yes: logger.info("User agreed to update the card data from Scryfall. Performing update") - self.ui.action_download_card_data.trigger() + self.card_data_downloader.import_from_api() else: # If the user declines to perform the update now, allow them to perform it later by enabling the action. self.ui.action_download_card_data.setEnabled(True) def ask_user_about_application_update_policy(self): @@ -517,8 +518,8 @@ for url in mime_data.urls(): pixmap = QPixmap(url.toLocalFile()) if not pixmap.isNull(): if pixmap.width() != width or pixmap.height() != height: new_size = QSize(width, height) - pixmap = pixmap.scaled(new_size, transformMode=TransformationMode.SmoothTransformation) + pixmap = pixmap.scaled(new_size, mode=TransformationMode.SmoothTransformation) result.append(pixmap) return result Index: mtg_proxy_printer/ui/page_config_container.py ================================================================== --- mtg_proxy_printer/ui/page_config_container.py +++ mtg_proxy_printer/ui/page_config_container.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from functools import partial -from PyQt5.QtWidgets import QWidget +from PySide6.QtWidgets import QWidget from mtg_proxy_printer.ui.common import load_ui_from_file from mtg_proxy_printer.logger import get_logger try: Index: mtg_proxy_printer/ui/page_config_preview_area.py ================================================================== --- mtg_proxy_printer/ui/page_config_preview_area.py +++ mtg_proxy_printer/ui/page_config_preview_area.py @@ -14,12 +14,13 @@ # along with this program. If not, see . import enum from unittest.mock import MagicMock -from PyQt5.QtCore import pyqtSlot as Slot, QPersistentModelIndex -from PyQt5.QtGui import QColorConstants, QPainter, QPixmap +from PySide6.QtCore import Slot, QPersistentModelIndex +from PySide6.QtGui import QColorConstants, QPainter, QPixmap +from PySide6.QtWidgets import QWidget from mtg_proxy_printer.document_controller.page_actions import ActionNewPage from mtg_proxy_printer.document_controller.card_actions import ActionAddCard, ActionRemoveCards from mtg_proxy_printer.model.document_loader import PageLayoutSettings from mtg_proxy_printer.units_and_sizes import CardSizes, CardSize @@ -27,12 +28,10 @@ from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.model.carddb import Card, MTGSet from mtg_proxy_printer.ui.common import load_ui_from_file from mtg_proxy_printer.logger import get_logger -from PyQt5.QtWidgets import QWidget - try: from mtg_proxy_printer.ui.generated.page_config_preview_area import Ui_PageConfigPreviewArea except ModuleNotFoundError: Ui_PageConfigPreviewArea = load_ui_from_file("page_config_preview_area") Index: mtg_proxy_printer/ui/page_config_widget.py ================================================================== --- mtg_proxy_printer/ui/page_config_widget.py +++ mtg_proxy_printer/ui/page_config_widget.py @@ -16,12 +16,12 @@ import functools from functools import partial import math import typing -from PyQt5.QtCore import pyqtSlot as Slot, Qt, pyqtSignal as Signal -from PyQt5.QtWidgets import QGroupBox, QWidget, QDoubleSpinBox, QCheckBox, QLineEdit +from PySide6.QtCore import Slot, Qt, Signal +from PySide6.QtWidgets import QGroupBox, QWidget, QDoubleSpinBox, QCheckBox, QLineEdit import mtg_proxy_printer.settings from mtg_proxy_printer.ui.common import load_ui_from_file, BlockedSignals, highlight_widget from mtg_proxy_printer.model.document_loader import PageLayoutSettings from mtg_proxy_printer.units_and_sizes import CardSizes, PageType, unit_registry, ConfigParser, QuantityT @@ -60,19 +60,19 @@ layout_key = spinbox.objectName() spinbox.valueChanged[float].connect( partial(self.set_numerical_page_layout_item, page_layout, layout_key, "mm")) spinbox.valueChanged[float].connect(self.validate_paper_size_settings) spinbox.valueChanged[float].connect(self.on_page_layout_setting_changed) - spinbox.valueChanged[float].connect(partial(self.page_layout_changed.emit, page_layout)) + spinbox.valueChanged[float].connect(lambda: self.page_layout_changed.emit(page_layout)) for checkbox, _ in self._get_boolean_settings_widgets(): layout_key = checkbox.objectName() checkbox.stateChanged.connect(partial(self.set_boolean_page_layout_item, page_layout, layout_key)) - checkbox.stateChanged.connect(partial(self.page_layout_changed.emit, page_layout)) + checkbox.stateChanged.connect(lambda: self.page_layout_changed.emit(page_layout)) ui.document_name.textChanged.connect(partial(setattr, page_layout, "document_name")) - ui.document_name.textChanged.connect(partial(self.page_layout_changed.emit, page_layout)) + ui.document_name.textChanged.connect(lambda: self.page_layout_changed.emit(page_layout)) return page_layout @staticmethod def set_numerical_page_layout_item(page_layout: PageLayoutSettings, layout_key: str, unit: str, value: float): # Implementation note: This call is placed here, because stuffing it into a lambda defined within a while loop @@ -87,11 +87,14 @@ # Implementation note: This call is placed here, because stuffing it into a lambda defined within a while loop # somehow uses the wrong references and will set the attribute that was processed last in the loop. # This method can be used via functools.partial to reduce the signature to (CheckState) -> None, # which can be connected to the stateChanged signal just fine. # Also, functools.partial does not exhibit the same issue as the lambda expression shows. - setattr(page_layout, layout_key, value == CheckState.Checked) + # + # PySide6 maps the QCheckBox check states to proper Python enums, but the stateChanged Qt signal carries raw + # integers. To get the integers for comparison, the lambdas below require accessing the CheckState enum values. + setattr(page_layout, layout_key, value == CheckState.Checked.value) @Slot() def on_page_layout_setting_changed(self): """ Recomputes and updates the page capacity display, whenever any page layout widget changes. Index: mtg_proxy_printer/ui/page_renderer.py ================================================================== --- mtg_proxy_printer/ui/page_renderer.py +++ mtg_proxy_printer/ui/page_renderer.py @@ -15,13 +15,14 @@ import enum import typing from functools import partial -from PyQt5.QtCore import Qt, QEvent -from PyQt5.QtWidgets import QGraphicsView, QWidget, QAction -from PyQt5.QtGui import QWheelEvent, QKeySequence, QPalette, QResizeEvent +from PySide6.QtCore import Qt, QEvent +from PySide6.QtWidgets import QGraphicsView, QWidget +from PySide6.QtGui import QWheelEvent, QKeySequence, QPalette, QResizeEvent, QAction + from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.logger import get_logger from mtg_proxy_printer.ui.page_scene import RenderMode, PageScene Index: mtg_proxy_printer/ui/page_scene.py ================================================================== --- mtg_proxy_printer/ui/page_scene.py +++ mtg_proxy_printer/ui/page_scene.py @@ -2,14 +2,14 @@ import enum import functools import itertools import typing -from PyQt5.QtCore import Qt, QSizeF, QPointF, QRectF, pyqtSignal as Signal, QObject, pyqtSlot as Slot, \ +from PySide6.QtCore import Qt, QSizeF, QPointF, QRectF, Signal, QObject, Slot, \ QPersistentModelIndex, QModelIndex, QRect, QPoint, QSize -from PyQt5.QtGui import QPen, QColorConstants, QBrush, QColor, QPalette, QFontMetrics, QPixmap, QTransform, QPolygonF -from PyQt5.QtWidgets import QGraphicsItemGroup, QGraphicsItem, QGraphicsPixmapItem, QGraphicsRectItem, \ +from PySide6.QtGui import QPen, QColorConstants, QBrush, QColor, QPalette, QFontMetrics, QPixmap, QTransform, QPolygonF +from PySide6.QtWidgets import QGraphicsItemGroup, QGraphicsItem, QGraphicsPixmapItem, QGraphicsRectItem, \ QGraphicsLineItem, QGraphicsSimpleTextItem, QGraphicsScene, QGraphicsPolygonItem from mtg_proxy_printer.model.card_list import PageColumns from mtg_proxy_printer.model.carddb import Card, CardCorner from mtg_proxy_printer.model.document import Document Index: mtg_proxy_printer/ui/printing_filter_widgets.py ================================================================== --- mtg_proxy_printer/ui/printing_filter_widgets.py +++ mtg_proxy_printer/ui/printing_filter_widgets.py @@ -16,13 +16,13 @@ import abc from functools import partial from typing import List, Tuple -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QDesktopServices -from PyQt5.QtWidgets import QGroupBox, QWidget, QCheckBox, QPushButton +from PySide6.QtCore import QUrl +from PySide6.QtGui import QDesktopServices +from PySide6.QtWidgets import QGroupBox, QWidget, QCheckBox, QPushButton from mtg_proxy_printer.units_and_sizes import ConfigParser, SectionProxy from mtg_proxy_printer.ui.common import highlight_widget try: Index: mtg_proxy_printer/ui/progress_bar.py ================================================================== --- mtg_proxy_printer/ui/progress_bar.py +++ mtg_proxy_printer/ui/progress_bar.py @@ -11,12 +11,12 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from PyQt5.QtCore import pyqtSlot as Slot, Qt -from PyQt5.QtWidgets import QWidget, QLabel, QProgressBar +from PySide6.QtCore import Slot, Qt +from PySide6.QtWidgets import QWidget, QLabel, QProgressBar try: from mtg_proxy_printer.ui.generated.progress_bar import Ui_ProgressBar except ModuleNotFoundError: from mtg_proxy_printer.ui.common import load_ui_from_file Index: mtg_proxy_printer/ui/settings_window.py ================================================================== --- mtg_proxy_printer/ui/settings_window.py +++ mtg_proxy_printer/ui/settings_window.py @@ -15,13 +15,13 @@ import pathlib import typing from functools import partial -from PyQt5.QtCore import QStringListModel, pyqtSignal as Signal, Qt, QItemSelectionModel, QEvent, QObject, QTimer -from PyQt5.QtWidgets import QDialogButtonBox, QMessageBox, QWidget, QDialog -from PyQt5.QtGui import QIcon, QStandardItemModel, QResizeEvent +from PySide6.QtCore import QStringListModel, Signal, Qt, QItemSelectionModel, QEvent, QObject, QTimer +from PySide6.QtWidgets import QDialogButtonBox, QMessageBox, QWidget, QDialog +from PySide6.QtGui import QIcon, QStandardItemModel, QResizeEvent import mtg_proxy_printer.app_dirs from mtg_proxy_printer.units_and_sizes import ConfigParser from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.document_controller import DocumentAction Index: mtg_proxy_printer/ui/settings_window_pages.py ================================================================== --- mtg_proxy_printer/ui/settings_window_pages.py +++ mtg_proxy_printer/ui/settings_window_pages.py @@ -17,13 +17,13 @@ from functools import partial import pathlib import typing from abc import abstractmethod -from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot, QUrl, QStandardPaths, QStringListModel, Qt, QThreadPool -from PyQt5.QtGui import QDesktopServices, QStandardItem, QIcon -from PyQt5.QtWidgets import QWidget, QCheckBox, QFileDialog, QMessageBox, QApplication, QLineEdit +from PySide6.QtCore import Signal, Slot, QUrl, QStandardPaths, QStringListModel, Qt, QThreadPool +from PySide6.QtGui import QDesktopServices, QStandardItem, QIcon +from PySide6.QtWidgets import QWidget, QCheckBox, QFileDialog, QMessageBox, QApplication, QLineEdit import mtg_proxy_printer.app_dirs import mtg_proxy_printer.settings from mtg_proxy_printer.printing_filter_updater import PrintingFilterUpdater from mtg_proxy_printer.logger import get_logger @@ -67,11 +67,10 @@ class PageMetadata(typing.NamedTuple): text: str icon_name: OptStr tooltip: OptStr = None - class Page(QWidget): """The base class for settings page widgets. Defines the API used by the settings window""" def display_item(self) -> typing.Sequence[QStandardItem]: data = self.display_metadata() @@ -104,11 +103,11 @@ """Highlights GUI widgets with a state different from the given settings""" pass def clear_highlight(self): """Clears all GUI widget highlights.""" - for item in self.findChildren((QWidget,), options=Qt.FindChildOption.FindChildrenRecursively): # type: QWidget + for item in self.findChildren(QWidget, options=Qt.FindChildOption.FindChildrenRecursively): # type: QWidget item.setGraphicsEffect(None) class DebugSettingsPage(Page): Index: mtg_proxy_printer/units_and_sizes.py ================================================================== --- mtg_proxy_printer/units_and_sizes.py +++ mtg_proxy_printer/units_and_sizes.py @@ -26,11 +26,11 @@ import pint try: from pint.facets.plain.registry import QuantityT, UnitT except ImportError: # Compatibility with Pint 0.21 for Python 3.8 support QuantityT = UnitT = typing.Any -from PyQt5.QtCore import QSize +from PySide6.QtCore import QSize def _setup_units() -> typing.Tuple[pint.UnitRegistry, QuantityT]: registry = pint.UnitRegistry() resolution = registry.parse_expression("300dots/inch") Index: mtg_proxy_printer/update_checker.py ================================================================== --- mtg_proxy_printer/update_checker.py +++ mtg_proxy_printer/update_checker.py @@ -19,11 +19,11 @@ import typing import urllib.parse import urllib.error import ijson -from PyQt5.QtCore import QObject, pyqtSignal as Signal, QThreadPool +from PySide6.QtCore import QObject, Signal, QThreadPool from mtg_proxy_printer.argument_parser import Namespace import mtg_proxy_printer.meta_data from mtg_proxy_printer import settings from mtg_proxy_printer.model.carddb import CardDatabase Index: pyproject.toml ================================================================== --- pyproject.toml +++ pyproject.toml @@ -31,11 +31,11 @@ # ijson adds full compatibility with Python 3.11 in version 3.2 (3.1 is really slow on Py3.11). # and Python 3.12 support in 3.2.1. Require newer versions on those Python versions to avoid # falling back to the slow pure Python backend. dependencies = [ "platformdirs >= 2.6.0", - "PyQt5", + "PySide6_Essentials >= 6.7.0", "ijson >= 3.1.0; python_version < '3.11'", "ijson >= 3.2.0; python_version >= '3.11'", "ijson >= 3.2.1; python_version >= '3.12'", "pint < 0.22; python_version < '3.9'", # 0.22 dropped Py 3.8 support, thus Win7 support "pint >= 0.22; python_version >= '3.9'", # Requires 0.22 for the QuantityT and UnitT types @@ -55,21 +55,15 @@ "pytest", "pytest-cov", "pytest-timeout", "pytest-qt >= 2.0", "tox >= 4.0", - "PySide2; platform_system == 'Windows' and python_version < '3.12'", # Used for lrelease: Compiling translations - "PySide6_Essentials; platform_system == 'Windows' and python_version >= '3.12'", # Used for lrelease: Compiling translations "pillow; platform_system == 'Windows'", # Used to convert the app icon to .ico ] package = [ "build", - # cx_Freeze 7.2 seems to somehow break loading the compiled Qt resources, but only for PyQt5. - # Limit to 7.1 for now, until a fix is found - "cx_Freeze >= 6.11.1, < 7.2; platform.python_implementation == 'CPython'", - "PySide2; platform_system == 'Windows' and python_version < '3.12'", # Used for lrelease: Compiling translations - "PySide6_Essentials; platform_system == 'Windows' and python_version >= '3.12'", # Used for lrelease: Compiling translations + "cx_Freeze >= 6.11.1; platform.python_implementation == 'CPython'", "pillow; platform_system == 'Windows'", # Used to convert the app icon to .ico ] [project.urls] @@ -104,10 +98,10 @@ [build-system] requires = [ "setuptools >=61", "wheel", - "PyQt5", + "PySide6_Essentials >= 6.7.0", "build", "tox >= 4.0", ] build-backend = "setuptools.build_meta" Index: run_tests.bat ================================================================== --- run_tests.bat +++ run_tests.bat @@ -1,10 +1,10 @@ :: Runs the unit tests :: Create or activate the build environment -IF EXIST "venv" ( - call venv\Scripts\activate.bat +IF EXIST "venv-PySide6" ( + call venv-PySide6\Scripts\activate.bat ) ELSE ( call create_development_environment.bat ) tox run Index: run_tests.sh ================================================================== --- run_tests.sh +++ run_tests.sh @@ -1,5 +1,5 @@ #!/bin/bash -source venv/bin/activate +source venv-PySide6/bin/activate tox run deactivate Index: scripts/clean_windows_build.bat ================================================================== --- scripts/clean_windows_build.bat +++ scripts/clean_windows_build.bat @@ -1,105 +1,69 @@ -:: Copyright (C) 2022-2023 Thomas Hess -:: -:: This program is free software: you can redistribute it and/or modify -:: it under the terms of the GNU General Public License as published by -:: the Free Software Foundation, either version 3 of the License, or -:: (at your option) any later version. -:: -:: This program is distributed in the hope that it will be useful, -:: but WITHOUT ANY WARRANTY; without even the implied warranty of -:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -:: GNU General Public License for more details. -:: -:: You should have received a copy of the GNU General Public License -:: along with this program. If not, see . - - -:: Cleanup various items that bloat the application bundle. Mostly related to unused PyQt5 and Qt5 components -if "%1%"=="" ( - pushd build\exe* -) else ( - pushd "%1" -) - -rmdir /S /Q PyQt5.uic.widget-plugins - -pushd lib -del ijson\backends\python*.dll - - -pushd PyQt5 -:: All DLLs here are also in Qt5\bin\ -del *.dll -del QtRemoteObjects.pyd QtSerialPort.pyd QtSensors.pyd QtNetwork.pyd QtXml.pyd QtXmlPatterns.pyd pyrcc.pyd - -:: The sip bindings aren't used at runtime -rmdir /S /Q bindings - -pushd Qt5 - -:: Unused Qsci -rmdir /S /Q qsci - -:: Unused Qml bindings -rmdir /S /Q qml - - -:: Unused DLLs -pushd bin -del d3dcompiler_47.dll libEGL.dll libGLESv2.dll opengl32sw.dll Qt5Bluetooth.dll Qt5DBus.dll Qt5Designer.dll Qt5Help.dll -del Qt5Location.dll Qt5Multimedia.dll Qt5MultimediaWidgets.dll Qt5Network.dll Qt5Nfc.dll Qt5OpenGL.dll Qt5Positioning.dll -del Qt5PositioningQuick.dll Qt5Qml.dll Qt5QmlModels.dll Qt5QmlWorkerScript.dll Qt5Quick.dll Qt5Quick3D.dll -del Qt5Quick3DAssetImport.dll Qt5Quick3DRender.dll Qt5Quick3DRuntimeRender.dll Qt5Quick3DUtils.dll Qt5QuickControls2.dll -del Qt5QuickParticles.dll Qt5QuickShapes.dll Qt5QuickTemplates2.dll Qt5QuickTest.dll Qt5QuickWidgets.dll Qt5RemoteObjects.dll -del Qt5Sensors.dll Qt5SerialPort.dll Qt5Sql.dll Qt5Test.dll Qt5WebChannel.dll -del Qt5WebSockets.dll Qt5WebView.dll Qt5XmlPatterns.dll -del libcrypto-1_1-x64.dll libssl-1_1-x64.dll -popd - - -:: Unused plugins -pushd plugins - -:: Remove duplicated Qt5 base DLLs -FOR %%G IN ( printsupport platforms imageformats styles -) DO del %%G\Qt5*.dll - - -FOR %%G IN ( - assetimporters audio geometryloaders geoservices mediaservice playlistformats - position renderers sceneparsers sensorgestures sensors sqldrivers texttospeech webview -) DO rmdir /S /Q %%G -popd - - -:: Unused translations (of unused modules) -pushd translations -del qtxmlpatterns_*.qm -del qtconnectivity_*.qm -del qtdeclarative_*.qm -del qtlocation_*.qm -del qtmultimedia_*.qm -del qtquickcontrols_*.qm -del qtquickcontrols2_*.qm -del qtserialport_*.qm -del qtwebsockets_*.qm -popd - -:: leave Qt5 -popd - -:: Unused extension modules -del *.pyi -del QAxContainer.pyd QtBluetooth.pyd QtDBus.pyd QtDesigner.pyd QtHelp.pyd QtLocation.pyd QtMultimedia.pyd QtMultimediaWidgets.pyd -del QtOpenGL.pyd QtNfc.pyd QtPositioning.pyd QtQml.pyd QtQuick.pyd QtQuick3D.pyd QtQuickWidgets.pyd -del QtRemoteObjects.pydQtSensors.pydQtSerialPort.pyd QtSql.pyd QtTest.pyd QtTextToSpeech.pyd QtWebChannel.pyd -del QtWebSockets.pyd _QOpenGLFunctions_2_0.pyd _QOpenGLFunctions_2_1.pyd _QOpenGLFunctions_4_1_Core.pyd - -:: leave PyQt5 -popd -:: leave lib -popd -::leave build directory -popd - - +:: Copyright (C) 2022-2023 Thomas Hess +:: +:: This program is free software: you can redistribute it and/or modify +:: it under the terms of the GNU General Public License as published by +:: the Free Software Foundation, either version 3 of the License, or +:: (at your option) any later version. +:: +:: This program is distributed in the hope that it will be useful, +:: but WITHOUT ANY WARRANTY; without even the implied warranty of +:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +:: GNU General Public License for more details. +:: +:: You should have received a copy of the GNU General Public License +:: along with this program. If not, see . + +if "%1%"=="" ( + pushd build\exe* +) else ( + pushd "%1" +) + +pushd lib + +pushd PySide6 + +:: Don't need the executables, like Qt6 Designer, etc. +:: Delete all typing stubs +del *.exe *.pyi + +:: Remove unused components. Each consists of a pair of QtComponent.pyd and Qt6Component.dll +del Q*tSerialPort* Qt*DBus* Qt*Designer* Qt*JsonRpc* Qt*Labs* Qt*LanguageServer* Qt*Network* +del Qt*Qml* Qt*Quick* Qt*RemoteObjects* Qt*Sensors* Qt*Sql* Qt*Test* Qt*WebChannel* +del Qt*Bluetooth* Qt*Charts* Qt*Concurrent* Qt*DataVisualization* Qt*Graphs* Qt*HttpServer* +del Qt*Nfc* Qt*Positioning* Qt*Scxml* Qt*SerialBus* Qt*SerialPort* Qt*ShaderTools* +del Qt*StateMachine* Qt*Test* Qt*TextToSpeech* Qt*Web* Qt*3D* +del Qt*Help* Qt*Multimedia* Qt*Xml* + +:: Unused audio/video codec DLLs +del avformat-*.dll avutil-*.dll swresample-*.dll swscale-*.dll +:: Unused OpenGL bindings. The Qt6OpenGL DLLs are required and thus not removed +del QtOpenGL.pyd QtOpenGLWidgets.pyd opengl32sw.dll + + +pushd translations +:: Remove translations for unused/removed components +del assistant* designer* linguist* qtdeclarative* + +:: leave translations +popd + +pushd plugins +del /Q /S tls + +:: leave plugins +popd + +:: leave PySide6 +popd + + +del shiboken6\shiboken6*.lib +del email\architecture.rst + +:: leave lib +popd +::leave build directory +popd + + Index: scripts/compile_resources.py ================================================================== --- scripts/compile_resources.py +++ scripts/compile_resources.py @@ -71,20 +71,19 @@ iterable = iter(iterable) return list(iter(lambda: tuple(itertools.islice(iterable, chunk_size)), ())) def compile(): - command = ("pyrcc5", "-compress", "9", str(SOURCES_PATH)) # noqa # "pyrcc5" is a program name, not a typo + command = ("pyside6-rcc", "--compress", "9", "--generator", "python", str(SOURCES_PATH)) compiled = subprocess.check_output(command, universal_newlines=True) # type: str # The resource compiler outputs > 15000 lines with extremely low line length. # Reduce the file size by removing a good percentage of those line breaks blocks = compiled.split("\\\n") chunks = split_iterable(blocks, 7) joined_chunks = ("".join(items) for items in chunks) compiled = "\\\n".join(joined_chunks) TARGET_PATH.write_text(compiled, "utf-8") - def clean(): TARGET_PATH.unlink(missing_ok=True) Index: scripts/compile_ui_files.py ================================================================== --- scripts/compile_ui_files.py +++ scripts/compile_ui_files.py @@ -23,19 +23,17 @@ to provide type hinting and autocompletion for the Ui classes defined by the UI files. """ import argparse import ast -import io import itertools import textwrap from pathlib import Path import shutil +import subprocess from typing import Tuple, NamedTuple, TypeVar, Iterable, Union, Type, List, Any, Dict, Set -import PyQt5.uic - SOURCE_ROOT = Path(__file__).parent.parent # Checkout root directory MAIN_PACKAGE = SOURCE_ROOT / "mtg_proxy_printer" UI_SOURCE_PATH = MAIN_PACKAGE / "resources/ui" # UI files live here TARGET_PATH = MAIN_PACKAGE / "ui/generated" # Package containing generated modules/type hinting stubs T = TypeVar("T") @@ -74,10 +72,16 @@ def type_filter(any_: Iterable[Any], types: Union[Type[T], Tuple[Type[T], ...]]) -> Iterable[T]: return filter(lambda x: isinstance(x, types), any_) + +def create_python_package(location: Path, /): + """Creates an empty Python package at the given Path""" + location.mkdir(parents=True, exist_ok=True) + (location/"__init__.py").touch(exist_ok=True) + def compile_ui_files(args: Namespace, target_path: Path = TARGET_PATH, source_path: Path = UI_SOURCE_PATH): """ Compiles all UI files found in source_path to Python types, storing results in target_path. @@ -84,32 +88,16 @@ Recursively finds UI files under source_path, replicates the found directory tree as a Python package hierarchy and populates it with the compiled Ui types. """ if args.purge_existing and target_path.is_dir(): shutil.rmtree(target_path) - - source_path = source_path.resolve() - target_path.mkdir(exist_ok=True) - - def map_to_output(directory, file_name): - dir_path = Path(directory).relative_to(source_path) - return target_path/dir_path, file_name - import functools - PyQt5.uic.open = functools.partial(open, encoding="utf-8") - PyQt5.uic.compileUiDir(str(source_path), recurse=True, map=map_to_output) create_python_package(target_path) - - -def create_python_package(target_dir: Path): - """ - Creates an empty __init__.py file in target_dir and each subdirectory, recursively. - This marks these directories as proper Python packages. - """ - (target_dir/"__init__.py").touch(exist_ok=True) - for entry in target_dir.rglob("*"): - if entry.is_dir(): - (entry/"__init__.py").touch(exist_ok=True) + for ui_file in source_path.rglob("*.ui"): + compiled = compile_ui_file(ui_file) + parent_dir = (target_path/ui_file.relative_to(source_path)).parent + create_python_package(parent_dir) + (parent_dir/f"{ui_file.stem}.py").write_text(compiled, "utf-8") def create_ui_type_stubs(args: Namespace, target_path: Path = TARGET_PATH, source_path: Path = UI_SOURCE_PATH): """ Creates type hinting stubs for all UI files found in source_path, storing results in target_path. @@ -118,17 +106,17 @@ populates it with the created type hints. """ if args.purge_existing and target_path.is_dir(): shutil.rmtree(target_path) class_registry = build_class_registry(MAIN_PACKAGE) + create_python_package(target_path) for ui_file in source_path.rglob("*.ui"): compiled = compile_ui_file(ui_file) stub = generate_stub(compiled, ui_file, class_registry) parent_dir = (target_path/ui_file.relative_to(source_path)).parent - parent_dir.mkdir(exist_ok=True) + create_python_package(parent_dir) (parent_dir/f"{ui_file.stem}.pyi").write_text(stub, "utf-8") - create_python_package(target_path) def build_class_registry(package_path: Path) -> ClassRegistry: """Scan the source tree for classes and build a dict from class name to import path""" result: ClassRegistry = {} @@ -139,16 +127,15 @@ result[class_def.name] = ast.ImportFrom(module_path, [ast.alias(class_def.name)]) return result def compile_ui_file(path: Path) -> str: - buffer = io.StringIO() try: - PyQt5.uic.compileUi(path, buffer, from_imports=True) + command = ("pyside6-uic", "--generator", "python", str(path)) except Exception as e: raise RuntimeError(f"Compilation failed for file {path}") from e - return buffer.getvalue() + return subprocess.check_output(command, encoding="utf-8") def generate_stub(compiled_ui: str, ui_file: Path, class_registry: ClassRegistry) -> str: root_node = ast.parse(compiled_ui) header = f"# Automatically generated type hinting stub for '{ui_file.name}'. Do not modify." @@ -213,26 +200,17 @@ def get_assignments(function_body: List[ast.stmt]) -> List[Assignment]: return [ Assignment( assignment.targets[0].attr, - get_assignment_type(assignment) + assignment.value.func.id ) for assignment in type_filter(function_body, ast.Assign) if hasattr(assignment.targets[0], "attr") # Filter out local variables ] - -def get_assignment_type(assignment: ast.Assign): - func = assignment.value.func - if isinstance(func, ast.Attribute): - return f"{func.value.id}.{func.attr}" # Qualified name: module.ClassName() - elif isinstance(func, ast.Name): - return func.id - raise NotImplementedError("Unknown assignment type") - def get_function_stub(function_body: ast.FunctionDef, found_class_uses: UsedClasses): for index, arg in enumerate(function_body.args.args): if arg.arg == "self": continue Index: scripts/update_translations.py ================================================================== --- scripts/update_translations.py +++ scripts/update_translations.py @@ -18,11 +18,10 @@ """ Management script for application translations """ import argparse -import itertools import pathlib import re import subprocess from typing import Callable, NamedTuple @@ -29,11 +28,10 @@ # Mapping between source locales, as provided by Crowdin, and the target, as expected/loaded by Qt. # TODO: Investigate, how systems behave in locales requiring the country as disambiguation, like en or zh. LOCALES = { "de-DE": "de", -# "en-GB": "en_GB", "en-US": "en_US", "es-ES": "es", "fr-FR": "fr", "it-IT": "it", "ja-JP": "ja", @@ -51,10 +49,11 @@ re.search( r'"source":\s*"(?P.+)",', crowdin_yml_path.read_text("utf-8") )["path"] ) + class Namespace(NamedTuple): """Mock namespace for type hinting""" command: Callable[["Namespace"], None] @@ -87,29 +86,18 @@ raise RuntimeError("The required Crowdin CLI client is not installed in the PATH, exiting.") from e def register_new_raw_strings(): TRANSLATIONS_DIR.mkdir(parents=True, exist_ok=True) - # PyQt5 - package = pathlib.Path("mtg_proxy_printer") - files = list(itertools.chain(package.rglob("*.py"), package.rglob("*.ui"))) - subprocess.call([ - "pylupdate5", - "-noobsolete", "-verbose", - *files, - "-ts", SOURCES_PATH - ]) - ''' PySide6 subprocess.call([ "pyside6-lupdate", "-source-language", "en_US", "-recursive", "-no-obsolete", "-extensions", "py,ui", "mtg_proxy_printer", "-ts", SOURCES_PATH ]) - ''' def upload_raw_strings(args: Namespace): """Updates the sources .ts from code, then uploads it to the Crowdin API""" register_new_raw_strings() @@ -137,15 +125,14 @@ except FileNotFoundError: print("lrelease not found on PATH. Falling back to the executable supplied by PySide2.") import sys exe = pathlib.Path(sys.executable) venv = exe.parent.parent - lrelease5 = venv / "Lib" / "site-packages" / "PySide2" / "lrelease.exe" lrelease6 = venv / "Lib" / "site-packages" / "PySide6" / "lrelease.exe" - if not lrelease5.is_file() and not lrelease6.is_file(): + if not lrelease6.is_file(): raise RuntimeError("No fallback lrelease executable found") - return lrelease5 if lrelease5.is_file() else lrelease6 + return lrelease6 else: return "lrelease" def compile_translations(args: Namespace): Index: setup_cx_freeze.py ================================================================== --- setup_cx_freeze.py +++ setup_cx_freeze.py @@ -41,57 +41,46 @@ base = "Win32GUI" if sys.platform == "win32" else None excludes = [ f"{main_package}.resources", # Do not include the raw resources as individual files "distutils", + "ijson.benchmark", # Ignore the benchmark script added after ijson 3.2.3 + "importlib_metadata", "lib2to3", "pep517", - "pytest", + "pint.testsuite", # Ignore the internal test suite "pydoc_data", + "pytest", + "sqlite3.test", # Ignore the internal test suite "tkinter", "toml", - "sqlite3.test", # Ignore the internal test suite - "pint.testsuite", # Ignore the internal test suite - "ijson.benchmark", # Ignore the benchmark script added after ijson 3.2.3 - "importlib_metadata", - - # All unused PyQt components - "PyQt5.QtXmlPatterns", - "PyQt5.QtNfc", - "PyQt5.QtQml", - "PyQt5.QtSql", - "PyQt5.Qt3DAnimation", - "PyQt5.Qt3DCore", - "PyQt5.Qt3DExtras", - "PyQt5.Qt3DInput", - "PyQt5.Qt3DLogic", - "PyQt5.Qt3DRender", - "PyQt5.QtBluetooth", - "PyQt5.QtChart", - "PyQt5.QtDataVisualisation", - "PyQt5.QtLocation", - "PyQt5.QtMultimedia", - "PyQt5.QtMultimediaWidgets", - "PyQt5.QtNetwork", - "PyQt5.QtNetworkAuth", - "PyQt5.QtOpenGL", - "PyQt5.QtPositioning", - "PyQt5.QtPurchasing", - "PyQt5.QtQuick", - "PyQt5.QtQuickWidgets", - "PyQt5.QtRemoteObjects", - "PyQt5.QtSensors", - "PyQt5.QtSerialPort", - "PyQt5.QtTest", - "PyQt5.QtWebChannel", - "PyQt5.QtWebEngine", - "PyQt5.QtWebEngineCore", - "PyQt5.QtWebEngineWidgets", - "PyQt5.QtWebKit", - "PyQt5.QtWebKitWidgets", - "PyQt5.QtWebSockets", - "PyQt5.uic.port_v2", + # Empty package with readme and download scripts + "ctypes.test", + # Unused PySide6 components + "PySide6.glue", + "PySide6.include", + "PySide6.metatypes", + "PySide6.plugins.assetimporters", + "PySide6.plugins.canbus", + "PySide6.plugins.designer", + "PySide6.plugins.geometryloaders", + "PySide6.plugins.geoservices", + "PySide6.plugins.multimedia", + "PySide6.plugins.networkinformation", + "PySide6.plugins.position", + "PySide6.plugins.qmltooling", + "PySide6.plugins.scxmldatamodel", + "PySide6.plugins.sensors", + "PySide6.plugins.sqldrivers", # Use Python native sqlite3 module instead + "PySide6.plugins.tls", + "PySide6.qml", + "PySide6.QtAsyncio", + "PySide6.resources", + "PySide6.scripts", + "PySide6.support", + "PySide6.translations.qtwebengine_locales", + "PySide6.typesystems", ] if sys.platform == "win32": excludes += [ "platformdirs.android", Index: tests/conftest.py ================================================================== --- tests/conftest.py +++ tests/conftest.py @@ -20,11 +20,11 @@ import itertools import sqlite3 import unittest.mock from pathlib import Path -from PyQt5.QtGui import QColorConstants, QPixmap +from PySide6.QtGui import QColorConstants, QPixmap import pytest import mtg_proxy_printer.sqlite_helpers import mtg_proxy_printer.settings from mtg_proxy_printer.printing_filter_updater import PrintingFilterUpdater @@ -55,11 +55,11 @@ db.execute("PRAGMA reverse_unordered_selects = TRUE") return db @pytest.fixture -def image_db(tmp_path: Path): +def image_db(qtbot, tmp_path: Path): image_db = ImageDatabase(tmp_path) regular_width, regular_height = image_db.blank_image.width(), image_db.blank_image.height() for scryfall_id, is_front in itertools.product( ["0000579f-7b35-4ed3-b44c-db2a538066fe", "b3b87bfc-f97f-4734-94f6-e3e2f335fc4d"], [True, False]): # Regular card images @@ -71,21 +71,19 @@ key = ImageKey(scryfall_id, True, True) image_db.loaded_images[key] = image_db.blank_image.scaled(regular_height, regular_width*2) image_db.images_on_disk.add(key) yield image_db - image_db.__dict__.clear() @pytest.fixture def document(qtbot, card_db: CardDatabase, image_db: ImageDatabase) -> Document: fill_card_database_with_json_cards(qtbot, card_db, [ "regular_english_card", "oversized_card", "english_double_faced_card"]) document = Document(card_db, image_db) document.loader.db = card_db.db yield document - document.__dict__.clear() @pytest.fixture def document_light(qtbot) -> Document: mock_card_db = unittest.mock.NonCallableMagicMock() @@ -95,6 +93,5 @@ mock_card_db.db = mtg_proxy_printer.sqlite_helpers.create_in_memory_database( "carddb", CardDatabase.MIN_SUPPORTED_SQLITE_VERSION, check_same_thread=False) document = Document(mock_card_db, mock_image_db) document.loader.db = mock_card_db.db yield document - document.__dict__.clear() Index: tests/document_controller/test_action_move_cards.py ================================================================== --- tests/document_controller/test_action_move_cards.py +++ tests/document_controller/test_action_move_cards.py @@ -16,11 +16,11 @@ from functools import partial import pytest from hamcrest import * -from PyQt5.QtCore import QModelIndex +from PySide6.QtCore import QModelIndex from mtg_proxy_printer.model.document_page import PageType from mtg_proxy_printer.document_controller import IllegalStateError from mtg_proxy_printer.document_controller.page_actions import ActionNewPage from mtg_proxy_printer.document_controller.move_cards import ActionMoveCards Index: tests/document_controller/test_action_remove_page.py ================================================================== --- tests/document_controller/test_action_remove_page.py +++ tests/document_controller/test_action_remove_page.py @@ -14,11 +14,11 @@ # along with this program. If not, see . from functools import partial from hamcrest import * -from PyQt5.QtCore import QModelIndex +from PySide6.QtCore import QModelIndex from mtg_proxy_printer.model.document_page import Page from mtg_proxy_printer.document_controller import IllegalStateError from mtg_proxy_printer.document_controller.page_actions import ActionRemovePage Index: tests/test___main__.py ================================================================== --- tests/test___main__.py +++ tests/test___main__.py @@ -43,11 +43,11 @@ def main_mocks(): with patch("mtg_proxy_printer.__main__.mtg_proxy_printer.logger.configure_root_logger") as configure_root_logger, \ patch.multiple( "mtg_proxy_printer.__main__", _app=DEFAULT, Application=DEFAULT, handle_ssl_certificates=DEFAULT, - parse_args=DEFAULT, QTimer=DEFAULT, logger=DEFAULT, QApplication=DEFAULT) as mocks: + parse_args=DEFAULT, QTimer=DEFAULT, logger=DEFAULT) as mocks: mocks["configure_root_logger"] = configure_root_logger yield mocks mocks.clear() @@ -79,11 +79,11 @@ main_mocks["configure_root_logger"].assert_called_once() def test_main_calls_exec_on_application_instance(main_mocks): mtg_proxy_printer.__main__.main() - main_mocks["Application"]().exec_.assert_called_once() + main_mocks["Application"]().exec.assert_called_once() def test_enqueues_startup_tasks_on_regular_launch(main_mocks): main_mocks["parse_args"].return_value = Namespace(test_exit_on_launch=False) mtg_proxy_printer.__main__.main() Index: tests/test_card_list.py ================================================================== --- tests/test_card_list.py +++ tests/test_card_list.py @@ -17,11 +17,11 @@ import typing from hamcrest import * import pytest from pytestqt.qtbot import QtBot -from PyQt5.QtCore import QItemSelectionModel +from PySide6.QtCore import QItemSelectionModel from mtg_proxy_printer.model.carddb import CardDatabase, CardIdentificationData from mtg_proxy_printer.model.card_list import CardListModel from tests.helpers import fill_card_database_with_json_cards Index: tests/test_carddb.py ================================================================== --- tests/test_carddb.py +++ tests/test_carddb.py @@ -198,12 +198,11 @@ "english_double_faced_art_series_card", "Flowerfoot_Swordmaster_card", "Flowerfoot_Swordmaster_token", ], ) - yield card_db - card_db.__dict__.clear() + return card_db def generate_test_cases_for_test_translate_card_name(): """Yields tuples with card data, target language and expected result.""" # Same-language identity translation Index: tests/test_check_card_rendering.py ================================================================== --- tests/test_check_card_rendering.py +++ tests/test_check_card_rendering.py @@ -14,11 +14,11 @@ # along with this program. If not, see . import pytest from hamcrest import * from pytestqt.qtbot import QtBot -from PyQt5.QtGui import QPixmap, QColorConstants +from PySide6.QtGui import QPixmap, QColorConstants from mtg_proxy_printer.model.carddb import Card, CheckCard, MTGSet from mtg_proxy_printer.units_and_sizes import CardSizes Index: tests/test_document.py ================================================================== --- tests/test_document.py +++ tests/test_document.py @@ -18,12 +18,13 @@ import pathlib import typing import unittest.mock import textwrap -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QPixmap +from PySide6.QtCore import Qt +from PySide6.QtGui import QPixmap + from hamcrest import * from hamcrest import contains_exactly import pytest from pytestqt.qtbot import QtBot @@ -360,12 +361,11 @@ margin_top=20*mm, margin_bottom=19*mm, margin_left=18*mm, margin_right=17*mm, row_spacing=3*mm, column_spacing=2*mm, card_bleed=1*mm, draw_cut_markers=True, draw_sharp_corners=False, ) document.apply(ActionEditDocumentSettings(custom_layout)) - yield document - document.__dict__.clear() + return document def test_document_reset_clears_modified_page_layout(qtbot: QtBot, document_custom_layout: Document): default_layout = PageLayoutSettings.create_from_settings() assert_that( Index: tests/test_image_db.py ================================================================== --- tests/test_image_db.py +++ tests/test_image_db.py @@ -13,12 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import io -from PyQt5.QtCore import QBuffer, QIODevice -from PyQt5.QtGui import QPixmap +from PySide6.QtCore import QBuffer, QIODevice +from PySide6.QtGui import QPixmap from hamcrest import * from pytestqt.qtbot import QtBot from mtg_proxy_printer.model.imagedb import ImageDatabase, ImageKey @@ -53,6 +53,5 @@ assert_that((image_db.db_path / keys[1].format_relative_path()).is_file(), is_(True)) assert_that((image_db.db_path / keys[0].format_relative_path()).parent.is_dir(), is_(True)) image_db.delete_disk_cache_entries([keys[1]]) assert_that((image_db.db_path / keys[1].format_relative_path()).is_file(), is_(False)) assert_that((image_db.db_path / keys[0].format_relative_path()).parent.is_dir(), is_(False)) - Index: tests/test_known_card_image_model.py ================================================================== --- tests/test_known_card_image_model.py +++ tests/test_known_card_image_model.py @@ -18,11 +18,11 @@ """ import pathlib import typing -from PyQt5.QtCore import Qt +from PySide6.QtCore import Qt import pytest from hamcrest import * from mtg_proxy_printer.ui.cache_cleanup_wizard import KnownCardImageModel, KnownCardColumns from mtg_proxy_printer.model.carddb import CardDatabase @@ -50,11 +50,10 @@ front_image.parent.mkdir(parents=True) back_image.parent.mkdir(parents=True) image_db.blank_image.save(str(front_image), "PNG") image_db.blank_image.save(str(back_image), "PNG") yield Environment(card_db, image_db, front_image, back_image) - image_db.__dict__.clear() @pytest.mark.parametrize("is_hidden", [True, False]) @pytest.mark.parametrize("is_front", [True, False]) def test_add_row_identifies_low_resolution_images(environment: Environment, is_front: bool, is_hidden: bool): Index: tests/test_page_layout_settings.py ================================================================== --- tests/test_page_layout_settings.py +++ tests/test_page_layout_settings.py @@ -19,12 +19,12 @@ import mtg_proxy_printer.model.document import mtg_proxy_printer.model.document_loader from mtg_proxy_printer.units_and_sizes import PageType, QuantityT, UnitT, unit_registry, StrDict from mtg_proxy_printer.ui.page_scene import RenderMode -from PyQt5.QtGui import QPageLayout, QPageSize -from PyQt5.QtCore import QMarginsF +from PySide6.QtGui import QPageLayout, QPageSize +from PySide6.QtCore import QMarginsF import pytest from hamcrest import * PageLayoutSettings = mtg_proxy_printer.model.document_loader.PageLayoutSettings from tests.hasgetter import has_getters Index: tests/ui/settings/test_card_filter_widgets.py ================================================================== --- tests/ui/settings/test_card_filter_widgets.py +++ tests/ui/settings/test_card_filter_widgets.py @@ -14,11 +14,11 @@ # along with this program. If not, see . import typing from unittest.mock import patch -from PyQt5.QtWidgets import QCheckBox +from PySide6.QtWidgets import QCheckBox import pytest from hamcrest import * from mtg_proxy_printer.units_and_sizes import SectionProxy import mtg_proxy_printer.settings Index: tests/ui/settings/test_initial_page_selection.py ================================================================== --- tests/ui/settings/test_initial_page_selection.py +++ tests/ui/settings/test_initial_page_selection.py @@ -11,11 +11,11 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from PyQt5.QtCore import QStringListModel +from PySide6.QtCore import QStringListModel from hamcrest import * from pytestqt.qtbot import QtBot from mtg_proxy_printer.ui.settings_window import SettingsWindow Index: tests/ui/test_add_card.py ================================================================== --- tests/ui/test_add_card.py +++ tests/ui/test_add_card.py @@ -17,12 +17,12 @@ import pytest from hamcrest import * from pytestqt.qtbot import QtBot -from PyQt5.QtCore import Qt, QPoint, QRect, QItemSelectionModel -from PyQt5.QtWidgets import QDialogButtonBox +from PySide6.QtCore import Qt, QPoint, QRect, QItemSelectionModel +from PySide6.QtWidgets import QDialogButtonBox from mtg_proxy_printer.model.carddb import CardDatabase, CardIdentificationData from mtg_proxy_printer.ui.add_card import HorizontalAddCardWidget, VerticalAddCardWidget from tests.helpers import fill_card_database_with_json_card @@ -44,13 +44,18 @@ expected_card_identification_data = CardIdentificationData( "en", "Clearwater Pathway", "aznr", "25" ) qtbot.add_widget(add_card_widget := widget_class()) add_card_widget.set_card_database(card_db) - add_card_widget.ui.copies_input.setValue(1) + add_card_widget.card_name_filter_updated("") # Populate the card name list + add_card_widget.ui.card_name_list.setSelection(QRect(1, 1, 1, 1), ClearAndSelect) + qtbot.mouseClick(add_card_widget.ui.card_name_list, LeftButton, pos=QPoint(10, 10)) + ok_button = add_card_widget.ui.button_box.button(StandardButton.Ok) + qtbot.mouseClick(ok_button, LeftButton, pos=QPoint(10, 10)) add_card_widget.ui.card_name_list.setSelection(QRect(1, 1, 1, 1), ClearAndSelect) qtbot.mouseClick(add_card_widget.ui.card_name_list, LeftButton, pos=QPoint(10, 10)) qtbot.wait(10) qtbot.mouseClick( add_card_widget.ui.button_box.button(StandardButton.Ok), LeftButton ) + add_card_widget.ui.copies_input.setValue(1) assert_that(add_card_widget._read_card_data_from_ui(), is_(equal_to(expected_card_identification_data))) Index: tests/ui/test_card_item.py ================================================================== --- tests/ui/test_card_item.py +++ tests/ui/test_card_item.py @@ -13,13 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from hamcrest import * import pytest -from PyQt5.QtCore import QSize -from PyQt5.QtGui import QColorConstants, QPixmap, QImage, QPainter, QColor -from PyQt5.QtWidgets import QGraphicsItem, QGraphicsScene +from PySide6.QtCore import QSize +from PySide6.QtGui import QColorConstants, QPixmap, QImage, QPainter, QColor +from PySide6.QtWidgets import QGraphicsItem, QGraphicsScene from mtg_proxy_printer.ui.page_scene import CardItem from tests.document_controller.helpers import append_new_card_in_page from tests.hasgetter import has_getter Index: tests/ui/test_central_widget.py ================================================================== --- tests/ui/test_central_widget.py +++ tests/ui/test_central_widget.py @@ -16,11 +16,12 @@ from pathlib import PurePath from unittest.mock import NonCallableMagicMock, patch import pytest from pytestqt.qtbot import QtBot -from PyQt5.QtCore import Qt +from PySide6.QtCore import Qt + from hamcrest import * from mtg_proxy_printer.model.document_page import Page from mtg_proxy_printer.model.carddb import Card, MTGSet, CheckCard from mtg_proxy_printer.document_controller.card_actions import ActionAddCard Index: tests/ui/test_deck_import_wizard.py ================================================================== --- tests/ui/test_deck_import_wizard.py +++ tests/ui/test_deck_import_wizard.py @@ -20,13 +20,13 @@ from hamcrest import * from pytestqt.qtbot import QtBot import pytest -from PyQt5.QtCore import QStringListModel, Qt, QPoint, QObject -from PyQt5.QtWidgets import QCheckBox, QWizard, QTableView, QComboBox, QLineEdit -from PyQt5.QtTest import QTest +from PySide6.QtCore import QStringListModel, Qt, QPoint, QObject +from PySide6.QtWidgets import QCheckBox, QWizard, QTableView, QComboBox, QLineEdit +from PySide6.QtTest import QTest import mtg_proxy_printer.settings from mtg_proxy_printer.model.carddb import CardDatabase, CardIdentificationData, CardList from mtg_proxy_printer.ui.deck_import_wizard import DeckImportWizard from mtg_proxy_printer.decklist_parser.re_parsers import MTGOnlineParser, MTGArenaParser, \ Index: tests/ui/test_item_delegate.py ================================================================== --- tests/ui/test_item_delegate.py +++ tests/ui/test_item_delegate.py @@ -15,11 +15,11 @@ from collections import Counter import itertools from unittest.mock import NonCallableMagicMock -from PyQt5.QtWidgets import QComboBox +from PySide6.QtWidgets import QComboBox import pytest from hamcrest import * from mtg_proxy_printer.document_controller.card_actions import ActionAddCard from mtg_proxy_printer.model.carddb import CardDatabase, Card, MTGSet Index: tests/ui/test_main_window.py ================================================================== --- tests/ui/test_main_window.py +++ tests/ui/test_main_window.py @@ -15,13 +15,12 @@ import dataclasses import pathlib import unittest.mock - -from PyQt5.QtCore import QStringListModel, QThreadPool -from PyQt5.QtWidgets import QMessageBox +from PySide6.QtCore import QStringListModel, QThreadPool +from PySide6.QtWidgets import QMessageBox from pytestqt.qtbot import QtBot from hamcrest import * import pytest import mtg_proxy_printer.http_file @@ -60,12 +59,10 @@ qtbot.add_widget(main_window) with qtbot.wait_exposed(main_window, timeout=1000): main_window.show() yield main_window main_window.hide() - del cid - main_window.__dict__.clear() def test_main_window_hides_progress_bar_after_downloading_image_during_load( qtbot: QtBot, main_window: MainWindow): with unittest.mock.patch.object( # Mock all HTTP-specific I/O calls @@ -142,11 +139,10 @@ unittest.mock.patch.object(QThreadPool.globalInstance(), "start") as thread_pool_start, \ qtbot.assertNotEmitted(main_window.loading_state_changed): main_window.show_card_data_update_available_message_box(10000) thread_pool_start.assert_not_called() import_from_api.assert_not_called() - assert_that(ui.action_download_card_data.isEnabled(), is_(True)) def test_accepting_card_data_update_offer_results_in_performed_action(qtbot: QtBot, main_window: MainWindow): ui = main_window.ui ui.action_download_card_data.setEnabled(True) @@ -155,11 +151,10 @@ "question", return_value=StandardButton.Yes) as message_box, \ unittest.mock.patch.object(QThreadPool.globalInstance(), "start") as thread_pool_start: main_window.show_card_data_update_available_message_box(10000) message_box.assert_called_once() thread_pool_start.assert_called_once() - assert_that(ui.action_download_card_data.isEnabled(), is_(False)) def test_action_download_card_data_is_enabled_after_network_error(qtbot: QtBot, main_window: MainWindow): ui = main_window.ui ui.action_download_card_data.setEnabled(False) Index: tests/ui/test_page_config_container.py ================================================================== --- tests/ui/test_page_config_container.py +++ tests/ui/test_page_config_container.py @@ -11,11 +11,11 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from PyQt5.QtWidgets import QCheckBox, QDoubleSpinBox, QLineEdit +from PySide6.QtWidgets import QCheckBox, QDoubleSpinBox, QLineEdit import pytest from pytestqt.qtbot import QtBot from hamcrest import * Index: tests/ui/test_page_config_dialog.py ================================================================== --- tests/ui/test_page_config_dialog.py +++ tests/ui/test_page_config_dialog.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import pytest -from PyQt5.QtWidgets import QDialogButtonBox +from PySide6.QtWidgets import QDialogButtonBox from mtg_proxy_printer.ui.dialogs import DocumentSettingsDialog StandardButton = QDialogButtonBox.StandardButton def test__init__(qtbot, document_light): Index: tests/ui/test_page_config_widget.py ================================================================== --- tests/ui/test_page_config_widget.py +++ tests/ui/test_page_config_widget.py @@ -14,11 +14,11 @@ # along with this program. If not, see . from unittest.mock import patch import pint -from PyQt5.QtWidgets import QDoubleSpinBox, QCheckBox, QLineEdit +from PySide6.QtWidgets import QDoubleSpinBox, QCheckBox, QLineEdit from hamcrest import * import pytest from pytestqt.qtbot import QtBot @@ -120,10 +120,11 @@ line_edit: QLineEdit = getattr(ui, attribute_name) new_value = "Test" with qtbot.waitSignals([line_edit.textChanged, widget.page_layout_changed], timeout=100): line_edit.setText(new_value) assert_that(widget.page_layout, has_property(attribute_name, equal_to(new_value))) + ZeroMarginsSettings = { "paper-height": "297 mm", "paper-width": "210 mm", Index: tests/ui/test_page_renderer.py ================================================================== --- tests/ui/test_page_renderer.py +++ tests/ui/test_page_renderer.py @@ -15,12 +15,12 @@ from unittest.mock import patch import pytest from hamcrest import * -from PyQt5.QtCore import QEvent -from PyQt5.QtWidgets import QAction +from PySide6.QtCore import QEvent +from PySide6.QtGui import QAction from mtg_proxy_printer.ui.page_renderer import PageRenderer, ZoomDirection PATH_PREFIX = "mtg_proxy_printer.ui.page_renderer." @@ -52,6 +52,6 @@ @pytest.mark.parametrize("zoom_action, direction", [ ("zoom_in_action", ZoomDirection.IN), ("zoom_out_action", ZoomDirection.OUT)]) def test_renderer_zoom_action_triggers_zoom(renderer: PageRenderer, zoom_action: str, direction: ZoomDirection): action: QAction = getattr(renderer, zoom_action) action.trigger() - renderer._perform_zoom_step.assert_called_once_with(direction, False) + renderer._perform_zoom_step.assert_called_once_with(direction) Index: tests/ui/test_page_scene.py ================================================================== --- tests/ui/test_page_scene.py +++ tests/ui/test_page_scene.py @@ -21,13 +21,13 @@ from math import ceil from hamcrest import * import pytest -from PyQt5.QtWidgets import QGraphicsPixmapItem, QGraphicsLineItem -from PyQt5.QtGui import QPalette, QColorConstants, QPixmap, QImage, QColor, QPainter -from PyQt5.QtCore import QPoint +from PySide6.QtWidgets import QGraphicsPixmapItem, QGraphicsLineItem +from PySide6.QtGui import QPalette, QColorConstants, QPixmap, QImage, QColor, QPainter +from PySide6.QtCore import QPoint from mtg_proxy_printer.units_and_sizes import PageType, CardSizes, CardSize, UnitT, unit_registry, QuantityT from mtg_proxy_printer.ui.page_scene import RenderMode, PageScene from mtg_proxy_printer.document_controller.card_actions import ActionAddCard, ActionRemoveCards from mtg_proxy_printer.document_controller.compact_document import ActionCompactDocument Index: tests/ui/test_progress_bar.py ================================================================== --- tests/ui/test_progress_bar.py +++ tests/ui/test_progress_bar.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . from hamcrest import * import pytest -from PyQt5.QtWidgets import QWidget +from PySide6.QtWidgets import QWidget from pytestqt.qtbot import QtBot from mtg_proxy_printer.ui.progress_bar import ProgressBar from tests.hasgetter import has_getters