Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch port_pyside6 Excluding Merge-Ins
This is equivalent to a diff from cf0e6f4403 to 8b1031c310
2024-10-18
| ||
15:54 | Tests: Simplified asserts in test_document.py check-in: c9928b0eb7 user: thomas tags: trunk | |
2024-10-16
| ||
11:48 | Merge with trunk Leaf check-in: 8b1031c310 user: thomas tags: port_pyside6 | |
2024-10-09
| ||
13:08 | Clarify the scope of the default card language option in the settings. Add a note about card language of imported deck lists. check-in: cf0e6f4403 user: thomas tags: trunk | |
2024-10-03
| ||
14:45 | Implement asynchronous card database migrations with progress reporting. Implements [f460c3e0caf7b1dd] check-in: 40729f514a user: thomas tags: trunk | |
2024-09-14
| ||
12:57 | Release v0.29.1 check-in: 45b2a6f419 user: thomas tags: port_pyside6 | |
Changes to README.md.
︙ | ︙ | |||
86 87 88 89 90 91 92 | - Python >= 3.8 These external libraries are used in the code. They can be installed from PyPI. - `platformdirs` - `ijson` - `pint` | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | - Python >= 3.8 These external libraries are used in the code. They can be installed from PyPI. - `platformdirs` - `ijson` - `pint` - `PySide6` - `delegateto` - `PyHamcrest` - `cx_Freeze` (Stand-alone bundles only. Used by the installer for Windows®-based platforms.) ### System libraries - `SQLite3` >= 3.35.0 |
︙ | ︙ |
Changes to build_MTGProxyPrinter_packages.bat.
1 2 3 | :: Generate an application bundle using cx_Freeze :: Create or activate the build environment | | | | 1 2 3 4 5 6 7 8 9 10 11 12 | :: Generate an application bundle using cx_Freeze :: Create or activate the build environment 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 :: and an MSI-based installer in the dist directory. :: Also creates a cross-platform Python sdist and wheel package in the dist directory. |
︙ | ︙ |
Changes to build_MTGProxyPrinter_packages.sh.
1 | #!/bin/bash | | | 1 2 3 4 5 6 7 8 9 | #!/bin/bash ENVIRONMENT_NAME="venv-PySide6" # Generate an application bundle using cx_Freeze for Linux. if [ ! -e "${ENVIRONMENT_NAME}" ]; then ./create_development_environment.sh fi source "${ENVIRONMENT_NAME}/bin/activate" |
︙ | ︙ |
Changes to create_development_environment.bat.
|
| | | | 1 2 3 4 5 6 7 8 9 10 | python -m venv venv-PySide6 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 python -m piptools compile --extra dev -o requirements-dev.txt pyproject.toml python -m piptools compile --extra package -o requirements-package.txt pyproject.toml |
︙ | ︙ |
Changes to create_development_environment.sh.
1 | #!/bin/bash | | | 1 2 3 4 5 6 7 8 9 | #!/bin/bash ENVIRONMENT_NAME="venv-PySide6" if [ -e "${ENVIRONMENT_NAME}" ]; then echo "Removing already existing virtual environment." rm -r "${ENVIRONMENT_NAME}" fi python -m venv "${ENVIRONMENT_NAME}" |
︙ | ︙ |
Changes to doc/ThirdPartyLicenses.md.
︙ | ︙ | |||
507 508 509 510 511 512 513 | whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. | | | | < | < < | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. # 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 Note: This section only applies to application bundles and installers created using cx_Freeze, like the stand-alone installer provided for Windows®-based platforms. |
︙ | ︙ |
Changes to mtg_proxy_printer/__main__.py.
︙ | ︙ | |||
16 17 18 19 20 21 22 | # Import and implicitly load the settings first, before importing any modules that pull in GUI classes. import mtg_proxy_printer.settings import os import platform import sys | | < | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # Import and implicitly load the settings first, before importing any modules that pull in GUI classes. import mtg_proxy_printer.settings import os import platform import sys 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 import mtg_proxy_printer.natsort |
︙ | ︙ | |||
56 57 58 59 60 61 62 | def main(): global _app arguments = parse_args() mtg_proxy_printer.app_dirs.migrate_from_old_appdirs() mtg_proxy_printer.logger.configure_root_logger() handle_ssl_certificates() | < < < | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | def main(): global _app arguments = parse_args() mtg_proxy_printer.app_dirs.migrate_from_old_appdirs() mtg_proxy_printer.logger.configure_root_logger() handle_ssl_certificates() _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() logger.debug("Left event loop.") if __name__ == "__main__": main() |
Changes to mtg_proxy_printer/application.py.
︙ | ︙ | |||
19 20 21 22 23 24 25 | import pathlib import platform import shutil import sys from tempfile import mkdtemp import typing | | | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import pathlib import platform import shutil import sys from tempfile import mkdtemp import typing 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 import mtg_proxy_printer.model.document import mtg_proxy_printer.model.imagedb |
︙ | ︙ | |||
54 55 56 57 58 59 60 | class Application(QApplication): 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__}") | < < < < < < > > > > > > > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | class Application(QApplication): 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__}") 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() self._setup_icons() self.language_model = self._create_language_model() # TODO: Can this be removed? |
︙ | ︙ | |||
217 218 219 220 221 222 223 | logger.info( f"Loading localisations. System locale: {system_locale.name()}, selected locale: {locale.name()}. " 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}'") | | | 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | logger.info( f"Loading localisations. System locale: {system_locale.name()}, selected locale: {locale.name()}. " 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.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): logger.debug(f"{component} translation loaded successfully, installing it") self.installTranslator(translator) |
︙ | ︙ | |||
254 255 256 257 258 259 260 | theme_search_paths = QIcon.themeSearchPaths() theme_search_paths.append(mtg_proxy_printer.ui.common.ICON_PATH_PREFIX) QIcon.setThemeSearchPaths(theme_search_paths) QIcon.setThemeName("breeze") else: logger.debug(f"Using system-provided icon theme '{fallback_icon_theme}'") | < < | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | theme_search_paths = QIcon.themeSearchPaths() theme_search_paths.append(mtg_proxy_printer.ui.common.ICON_PATH_PREFIX) QIcon.setThemeSearchPaths(theme_search_paths) QIcon.setThemeName("breeze") else: logger.debug(f"Using system-provided icon theme '{fallback_icon_theme}'") @Slot() def quit(self): logger.info("About to exit.") self.should_run = False self.main_window.hide() self.main_window.close() self.closeAllWindows() |
︙ | ︙ |
Changes to mtg_proxy_printer/card_info_downloader.py.
︙ | ︙ | |||
24 25 26 27 28 29 30 | import socket import typing import urllib.error import urllib.parse import urllib.request import ijson | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import socket import typing import urllib.error import urllib.parse import urllib.request import ijson 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 import mtg_proxy_printer.metered_file from mtg_proxy_printer.logger import get_logger |
︙ | ︙ |
Changes to mtg_proxy_printer/carddb_migrations.py.
︙ | ︙ | |||
28 29 30 31 32 33 34 | import time import typing import urllib.error import urllib.parse from textwrap import dedent from typing import List, Dict, Union, Tuple, Any, Generator, Callable | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import time import typing import urllib.error import urllib.parse from textwrap import dedent from typing import List, Dict, Union, Tuple, Any, Generator, Callable from PySide6.QtCore import QCoreApplication, Qt try: from typing import LiteralString except AttributeError: from typing_extensions import LiteralString from mtg_proxy_printer.progress_meter import ProgressMeter |
︙ | ︙ |
Changes to mtg_proxy_printer/decklist_downloader.py.
︙ | ︙ | |||
24 25 26 27 28 29 30 | import urllib.parse from io import StringIO import platform import re import typing import ijson | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import urllib.parse from io import StringIO import platform import re import typing import ijson 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, \ XMageParser from mtg_proxy_printer.logger import get_logger |
︙ | ︙ |
Changes to mtg_proxy_printer/decklist_parser/common.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from abc import abstractmethod import typing | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from abc import abstractmethod import typing 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 logger = get_logger(__name__) del get_logger |
︙ | ︙ |
Changes to mtg_proxy_printer/decklist_parser/csv_parsers.py.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # along with this program. If not, see <http://www.gnu.org/licenses/>. import abc import collections import csv import typing | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # along with this program. If not, see <http://www.gnu.org/licenses/>. import abc import collections import csv import typing 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 from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) |
︙ | ︙ |
Changes to mtg_proxy_printer/decklist_parser/re_parsers.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import copy from collections import Counter import re import typing | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import copy from collections import Counter import re import typing 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 logger = get_logger(__name__) del get_logger |
︙ | ︙ | |||
170 171 172 173 174 175 176 | class MagicWorkstationDeckDataFormatParser(GenericRegularExpressionDeckParser): @staticmethod def supported_file_types() -> typing.Dict[str, typing.List[str]]: return { | | | | > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | class MagicWorkstationDeckDataFormatParser(GenericRegularExpressionDeckParser): @staticmethod def supported_file_types() -> typing.Dict[str, typing.List[str]]: return { 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, re.compile(r"(SB: {2})?(?P<copies>\d+) \[(?P<set_code>\w+)?] (?P<name>.+)"), parent ) |
︙ | ︙ | |||
249 250 251 252 253 254 255 | A parser for XMage deck files (file extension ".dck"). """ @staticmethod def supported_file_types() -> typing.Dict[str, typing.List[str]]: return { QCoreApplication.translate("XMageParser", "XMage Deck file"): ["dck"], | | > | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | A parser for XMage deck files (file extension ".dck"). """ @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<copies>\d+) \[(?P<set_code>\w+):(?P<collector_number>[^]]+)] (?P<name>.+)"), parent ) |
Changes to mtg_proxy_printer/document_controller/_interface.py.
︙ | ︙ | |||
15 16 17 18 19 20 21 | from abc import abstractmethod from functools import partial import itertools import operator import typing | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | from abc import abstractmethod from functools import partial import itertools import operator import typing 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 try: |
︙ | ︙ |
Changes to mtg_proxy_printer/document_controller/move_cards.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import functools import itertools import typing | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import functools import itertools import typing from PySide6.QtCore import QModelIndex from ._interface import DocumentAction, IllegalStateError, Self from mtg_proxy_printer.logger import get_logger if typing.TYPE_CHECKING: from mtg_proxy_printer.model.document_page import Page from mtg_proxy_printer.model.document import Document |
︙ | ︙ |
Changes to mtg_proxy_printer/document_controller/replace_card.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import functools import typing | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import functools import typing 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 from mtg_proxy_printer.model.document import Document |
︙ | ︙ |
Changes to mtg_proxy_printer/document_controller/shuffle_document.py.
︙ | ︙ | |||
18 19 20 21 22 23 24 | from random import randbytes except ImportError: # Compatibility with Py 3.8 from secrets import token_bytes as randbytes import typing | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from random import randbytes except ImportError: # Compatibility with Py 3.8 from secrets import token_bytes as randbytes import typing 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 from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.units_and_sizes import PageType |
︙ | ︙ |
Changes to mtg_proxy_printer/downloader_base.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. import gzip | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. import gzip 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 # Offer accepting gzip, as that is supported by the Scryfall server and reduces network data use by 80-90% |
︙ | ︙ |
Changes to mtg_proxy_printer/http_file.py.
︙ | ︙ | |||
22 23 24 25 26 27 28 | import socket import time import typing from typing import List, Optional, Dict import urllib.error import urllib.request | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import socket import time import typing from typing import List, Optional, Dict import urllib.error import urllib.request 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 logger = get_logger(__name__) del get_logger |
︙ | ︙ |
Changes to mtg_proxy_printer/meta_data.py.
︙ | ︙ | |||
10 11 12 13 14 15 16 | # 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 <http://www.gnu.org/licenses/>. PROGRAMNAME = "MTGProxyPrinter" | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 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 <http://www.gnu.org/licenses/>. PROGRAMNAME = "MTGProxyPrinter" __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})" |
Changes to mtg_proxy_printer/metered_file.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from typing import Iterable, List, Optional, BinaryIO, Union from io import BufferedIOBase | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from typing import Iterable, List, Optional, BinaryIO, Union from io import BufferedIOBase from PySide6.QtCore import QObject, Signal from delegateto import delegate from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) del get_logger __all__ = [ |
︙ | ︙ |
Changes to mtg_proxy_printer/missing_images_manager.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. import typing | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. import typing 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 __all__ = [ |
︙ | ︙ |
Changes to mtg_proxy_printer/model/card_list.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import enum import itertools import typing | | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import enum import itertools import typing 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__) del get_logger CardList = typing.List[Card] |
︙ | ︙ | |||
95 96 97 98 99 100 101 | return self.tr("Front") if card.is_front else self.tr("Back") if card.is_oversized: 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 | | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | return self.tr("Front") if card.is_front else self.tr("Back") if card.is_oversized: 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) -> ItemFlag: flags = super().flags(index) if index.column() in self.EDITABLE_COLUMNS: flags |= ItemFlag.ItemIsEditable return flags def setData(self, index: QModelIndex, value: typing.Any, role: ItemDataRole = ItemDataRole.EditRole) -> bool: column = index.column() |
︙ | ︙ |
Changes to mtg_proxy_printer/model/carddb.py.
︙ | ︙ | |||
20 21 22 23 24 25 26 | import itertools import functools import pathlib import sqlite3 import threading import typing | | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import itertools import functools import pathlib import sqlite3 import threading import typing 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 from mtg_proxy_printer.natsort import natural_sorted import mtg_proxy_printer.meta_data |
︙ | ︙ | |||
148 149 150 151 152 153 154 | sample_area = self.image_file.copy(QRect( QPoint( round(self.image_file.width() * corner.value[0]), round(self.image_file.height() * corner.value[1])), QSize(10, 10) )) average_color = sample_area.scaled( | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | sample_area = self.image_file.copy(QRect( QPoint( 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, 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}]' @property def set_code(self): |
︙ | ︙ | |||
225 226 227 228 229 230 231 | # Cards thus can’t be scaled using a singular factor of sqrt(2) on both axis. # The scaled cards get a bit compressed horizontally. 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) | | | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | # Cards thus can’t be scaled using a singular factor of sqrt(2) on both axis. # The scaled cards get a bit compressed horizontally. 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.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) painter.drawPixmap(QPointF(0, -card_size.height()), self.front.image_file) |
︙ | ︙ |
Changes to mtg_proxy_printer/model/document.py.
︙ | ︙ | |||
19 20 21 22 23 24 25 | import itertools import math import pathlib import sys import textwrap import typing | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import itertools import math import pathlib import sys import textwrap import typing 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 from mtg_proxy_printer.model.carddb import AnyCardType, CardDatabase, CardIdentificationData from mtg_proxy_printer.model.card_list import PageColumns |
︙ | ︙ |
Changes to mtg_proxy_printer/model/document_loader.py.
︙ | ︙ | |||
22 23 24 25 26 27 28 | import pathlib import sqlite3 import textwrap import typing from unittest.mock import patch import pint | | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import pathlib import sqlite3 import textwrap import typing from unittest.mock import patch import pint 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 except ImportError: # Compatibility with PyHamcrest < 1.10 |
︙ | ︙ |
Changes to mtg_proxy_printer/model/imagedb.py.
︙ | ︙ | |||
22 23 24 25 26 27 28 | import shutil import socket import string import threading import typing import urllib.error | | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import shutil import socket import string import threading import typing import urllib.error 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 from mtg_proxy_printer.document_controller.card_actions import ActionAddCard from mtg_proxy_printer.document_controller.replace_card import ActionReplaceCard |
︙ | ︙ |
Changes to mtg_proxy_printer/model/string_list.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. import typing | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. import typing from PySide6.QtCore import QAbstractListModel, Qt, QObject, QModelIndex from mtg_proxy_printer.model.carddb import MTGSet __all__ = [ "PrettySetListModel", ] |
︙ | ︙ |
Changes to mtg_proxy_printer/natsort.py.
︙ | ︙ | |||
16 17 18 19 20 21 22 | """ Natural sorting for lists or other iterables of strings. """ import re import typing | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | """ Natural sorting for lists or other iterables of strings. """ import re import typing from PySide6.QtCore import QSortFilterProxyModel, QModelIndex __all__ = [ "natural_sorted", "str_less_than", "NaturallySortedSortFilterProxyModel", ] |
︙ | ︙ |
Changes to mtg_proxy_printer/print.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import math from pathlib import Path | | | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import math from pathlib import Path 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 from mtg_proxy_printer.ui.page_scene import RenderMode, PageScene from mtg_proxy_printer.logger import get_logger |
︙ | ︙ | |||
59 60 61 62 63 64 65 | f"Setting page layout failed! " f"Layout: page_size={page_layout.pageSize().size(QPageSize.Unit.Millimeter)}, " 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 | < | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | f"Setting page layout failed! " f"Layout: page_size={page_layout.pageSize().size(QPageSize.Unit.Millimeter)}, " 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.setDuplex(QPrinter.DuplexMode.DuplexNone) printer.setOutputFormat(QPrinter.OutputFormat.NativeFormat) if RenderMode.IMPLICIT_MARGINS not in renderer.render_mode: printer.setFullPage(True) return printer |
︙ | ︙ |
Changes to mtg_proxy_printer/printing_filter_updater.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import sqlite3 import typing | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import sqlite3 import typing 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 from mtg_proxy_printer.model.carddb import SCHEMA_NAME, with_database_write_lock from mtg_proxy_printer.sqlite_helpers import cached_dedent, open_database |
︙ | ︙ |
Changes to mtg_proxy_printer/resources/ui/central_widget/columnar.ui.
︙ | ︙ | |||
22 23 24 25 26 27 28 | <verstretch>1</verstretch> </sizepolicy> </property> <property name="acceptDrops"> <bool>false</bool> </property> <property name="renderHints"> | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <verstretch>1</verstretch> </sizepolicy> </property> <property name="acceptDrops"> <bool>false</bool> </property> <property name="renderHints"> <set>QPainter::Antialiasing</set> </property> </widget> </item> <item row="4" column="2" rowspan="2"> <widget class="QTableView" name="page_card_table_view"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> |
︙ | ︙ |
Changes to mtg_proxy_printer/resources/ui/central_widget/grouped.ui.
︙ | ︙ | |||
22 23 24 25 26 27 28 | <verstretch>10</verstretch> </sizepolicy> </property> <property name="acceptDrops"> <bool>false</bool> </property> <property name="renderHints"> | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <verstretch>10</verstretch> </sizepolicy> </property> <property name="acceptDrops"> <bool>false</bool> </property> <property name="renderHints"> <set>QPainter::Antialiasing</set> </property> </widget> </item> <item row="1" column="0" rowspan="7" colspan="2"> <widget class="QListView" name="document_view"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> |
︙ | ︙ |
Changes to mtg_proxy_printer/resources/ui/document_settings_dialog.ui.
1 2 3 4 | <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>DocumentSettingsDialog</class> <widget class="QDialog" name="DocumentSettingsDialog"> | < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>DocumentSettingsDialog</class> <widget class="QDialog" name="DocumentSettingsDialog"> <property name="windowTitle"> <string>Set Document settings</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="PageConfigContainer" name="page_config_container" native="true"/> </item> |
︙ | ︙ | |||
40 41 42 43 44 45 46 | <resources/> <connections> <connection> <sender>button_box</sender> <signal>accepted()</signal> <receiver>DocumentSettingsDialog</receiver> <slot>accept()</slot> | < < < < < < < < < < < < < < < < < < < < | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <resources/> <connections> <connection> <sender>button_box</sender> <signal>accepted()</signal> <receiver>DocumentSettingsDialog</receiver> <slot>accept()</slot> </connection> <connection> <sender>button_box</sender> <signal>rejected()</signal> <receiver>DocumentSettingsDialog</receiver> <slot>reject()</slot> </connection> </connections> </ui> |
Changes to mtg_proxy_printer/runner.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. import typing | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. import typing from PySide6.QtCore import QRunnable, QObject, Signal from mtg_proxy_printer.logger import get_logger logger = get_logger(__name__) del get_logger __all__ = [ "Runnable", |
︙ | ︙ |
Changes to mtg_proxy_printer/settings.py.
︙ | ︙ | |||
17 18 19 20 21 22 23 | import math import pathlib import re import typing import tokenize import pint | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import math import pathlib import re import typing import tokenize import pint 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 __all__ = [ |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/add_card.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. from typing import Union, Type, Optional | | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # 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 <http://www.gnu.org/licenses/>. from typing import Union, Type, Optional 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 import mtg_proxy_printer.settings from mtg_proxy_printer.ui.common import load_ui_from_file |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/cache_cleanup_wizard.py.
︙ | ︙ | |||
17 18 19 20 21 22 23 | import datetime import enum import functools import math import pathlib import typing | | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import datetime import enum import functools import math import pathlib import typing 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 from mtg_proxy_printer.ui.common import load_ui_from_file, format_size, WizardBase from mtg_proxy_printer.units_and_sizes import OptStr |
︙ | ︙ | |||
59 60 61 62 63 64 65 | source = QPixmap(str(path)) pixmap = source.scaled( 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) | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | source = QPixmap(str(path)) pixmap = source.scaled( 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().toStdString() card_name = f'<p style="text-align:center">{card_name}</p><br>' if card_name else "" tooltip_text = f'{card_name}<img src="data:image/png;base64,{image}">' return tooltip_text class KnownCardColumns(enum.IntEnum): Name = 0 |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/central_widget.py.
︙ | ︙ | |||
15 16 17 18 19 20 21 | import functools import math import operator import pathlib from typing import Union, Type, Optional | | | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import functools import math import operator import pathlib from typing import Union, Type, Optional from PySide6.QtCore import Signal, Slot, QPersistentModelIndex, QItemSelectionModel, \ QModelIndex, QPoint, Qt 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 from mtg_proxy_printer.model.carddb import CardDatabase, Card, CardList, CheckCard, AnyCardType, AnyCardTypeForTypeCheck from mtg_proxy_printer.model.imagedb import ImageDatabase |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/common.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pathlib import platform import typing | | | | < | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pathlib import platform import typing 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 __all__ = [ "RESOURCE_PATH_PREFIX", |
︙ | ︙ | |||
90 91 92 93 94 95 96 | if not display_text: display_text = str(path) label.setText(f"""<a href="{url.path(QUrl.FullyEncoded):s}">{display_text:s}</a>""") def load_ui_from_file(name: str): """ | | < > | > > | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | if not display_text: display_text = str(path) label.setText(f"""<a href="{url.path(QUrl.FullyEncoded):s}">{display_text:s}</a>""") def load_ui_from_file(name: str): """ 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) 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): error_message = f"Icon not found: {file_path}" logger.error(error_message) |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/compiled_resources.pyi.
︙ | ︙ | |||
10 11 12 13 14 15 16 | # 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 <http://www.gnu.org/licenses/>. """ | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # 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 <http://www.gnu.org/licenses/>. """ Declares the interface created by the PySide6 resource compiler. This is only used for type hinting. """ import typing qt_version: typing.List[int] = ... rcc_version: int = ... |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/deck_import_wizard.py.
︙ | ︙ | |||
18 19 20 21 22 23 24 | import math import pathlib import re import typing import urllib.error import urllib.parse | | | | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import math import pathlib import re import typing import urllib.error import urllib.parse from PySide6.QtCore import Slot, Signal, Property, QStringListModel, Qt, SIGNAL, \ QItemSelection, QSize, QUrl 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, \ get_downloader_class, ParserBase from mtg_proxy_printer.model.carddb import CardDatabase |
︙ | ︙ | |||
116 117 118 119 120 121 122 | ui.deck_list_download_url_line_edit.textChanged.connect( lambda text: ui.deck_list_download_button.setEnabled( 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) | | | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | ui.deck_list_download_url_line_edit.textChanged.connect( lambda text: ui.deck_list_download_button.setEnabled( 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) 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", "deck_list_downloader_changed(str)") self.registerField( "translate-deck-list-target-language", ui.translate_deck_list_target_language, "currentText", "currentTextChanged(str)" ) logger.info(f"Created {self.__class__.__name__} instance.") @Property(str, notify=deck_list_downloader_changed) def deck_list_downloader(self): return self._deck_list_downloader |
︙ | ︙ | |||
603 604 605 606 607 608 609 610 611 612 613 614 615 616 | QWizard.WizardButton.FinishButton: "dialog-ok", QWizard.WizardButton.CancelButton: "dialog-cancel", } 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.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) self.addPage(self.select_deck_parser_page) self.addPage(self.summary_page) | > | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | QWizard.WizardButton.FinishButton: "dialog-ok", QWizard.WizardButton.CancelButton: "dialog-cancel", } 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) self.addPage(self.select_deck_parser_page) self.addPage(self.summary_page) |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/dialogs.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import typing import pathlib import sys | | | | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import typing import pathlib import sys 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 import mtg_proxy_printer.print import mtg_proxy_printer.settings |
︙ | ︙ | |||
245 246 247 248 249 250 251 | file_path = self._get_file_path(":/changelog.md", "/../../doc/changelog.md") self._set_text_browser_with_markdown_file_content(file_path, self.ui.changelog_text_browser) 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: | | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | file_path = self._get_file_path(":/changelog.md", "/../../doc/changelog.md") self._set_text_browser_with_markdown_file_content(file_path, self.ui.changelog_text_browser) 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().toStdString() finally: file.close() text_browser.setMarkdown(content) class PrintPreviewDialog(QPrintPreviewDialog): |
︙ | ︙ | |||
365 366 367 368 369 370 371 | logger.info(f"User accepted the {self.__class__.__name__}") action = ActionEditDocumentSettings(self.ui.page_config_container.ui.page_config_widget.page_layout) self.document.apply(action) logger.debug("Saving settings in the document done.") def clear_highlight(self): """Clears all GUI widget highlights.""" | | | 365 366 367 368 369 370 371 372 373 | logger.info(f"User accepted the {self.__class__.__name__}") action = ActionEditDocumentSettings(self.ui.page_config_container.ui.page_config_widget.page_layout) 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 item.setGraphicsEffect(None) |
Changes to mtg_proxy_printer/ui/item_delegates.py.
︙ | ︙ | |||
10 11 12 13 14 15 16 | # 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 <http://www.gnu.org/licenses/>. import typing | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. import typing 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 logger = get_logger(__name__) |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/main_window.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pathlib import typing | > | | | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pathlib import typing 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 from mtg_proxy_printer.model.document import Document from mtg_proxy_printer.document_controller.compact_document import ActionCompactDocument |
︙ | ︙ | |||
121 122 123 124 125 126 127 | (self.ui.action_undo, StandardKey.Undo), (self.ui.action_redo, StandardKey.Redo), ] for action, shortcut in actions_with_shortcuts: action.setShortcut(shortcut) def _setup_central_widget(self): | < | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | (self.ui.action_undo, StandardKey.Undo), (self.ui.action_redo, StandardKey.Redo), ] for action, shortcut in actions_with_shortcuts: action.setShortcut(shortcut) def _setup_central_widget(self): 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) def _setup_undo_redo_actions(self, document: Document): |
︙ | ︙ | |||
262 263 264 265 266 267 268 | action_str = self.tr( "printing", "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) | > | | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | action_str = self.tr( "printing", "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) # 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( "printing", "This is passed as the {action} when asking the user about compacting the document if that can save pages") |
︙ | ︙ | |||
330 331 332 333 334 335 336 | self, self.tr("Download required Card data from Scryfall?"), self.tr( "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: | | | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | self, self.tr("Download required Card data from Scryfall?"), self.tr( "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.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: logger.debug("No save file path set. Call 'Save as' instead.") self.ui.action_save_as.trigger() |
︙ | ︙ | |||
425 426 427 428 429 430 431 | self, self.tr("New card data available"), self.tr( "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") | | | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | self, self.tr("New card data available"), self.tr( "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.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): """Executed on start when the application update policy setting is set to None, the default value.""" name = mtg_proxy_printer.meta_data.PROGRAMNAME |
︙ | ︙ | |||
515 516 517 518 519 520 521 | regular = mtg_proxy_printer.units_and_sizes.CardSizes.REGULAR width, height = regular.width.magnitude, regular.height.magnitude 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) | | | 516 517 518 519 520 521 522 523 524 525 | regular = mtg_proxy_printer.units_and_sizes.CardSizes.REGULAR width, height = regular.width.magnitude, regular.height.magnitude 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, mode=TransformationMode.SmoothTransformation) result.append(pixmap) return result |
Changes to mtg_proxy_printer/ui/page_config_container.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. from functools import partial | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. from functools import partial 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: from mtg_proxy_printer.ui.generated.page_config_container import Ui_PageConfigContainer except ModuleNotFoundError: |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/page_config_preview_area.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import enum from unittest.mock import MagicMock | | | > < < | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import enum from unittest.mock import MagicMock 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 from mtg_proxy_printer.model.document_page import PageType 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 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") logger = get_logger(__name__) del get_logger |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/page_config_widget.py.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # along with this program. If not, see <http://www.gnu.org/licenses/>. import functools from functools import partial import math import typing | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # along with this program. If not, see <http://www.gnu.org/licenses/>. import functools from functools import partial import math import typing 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 try: |
︙ | ︙ | |||
58 59 60 61 62 63 64 | for spinbox, _ in self._get_decimal_settings_widgets(): 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) | | | | > > > | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | for spinbox, _ in self._get_decimal_settings_widgets(): 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(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(lambda: self.page_layout_changed.emit(page_layout)) ui.document_name.textChanged.connect(partial(setattr, page_layout, "document_name")) 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 # 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 (float) -> None, # which can be connected to the valueChanged[float] signal just fine. # Also, functools.partial does not exhibit the same issue as the lambda expression shows. setattr(page_layout, layout_key, value*unit_registry.parse_units(unit)) @staticmethod def set_boolean_page_layout_item(page_layout: PageLayoutSettings, layout_key: str, value: CheckState): # 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. # # 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. """ regular_capacity = self.page_layout.compute_page_card_capacity(PageType.REGULAR) |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/page_renderer.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import enum import typing from functools import partial | | | | > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import enum import typing from functools import partial 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 logger = get_logger(__name__) del get_logger |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/page_scene.py.
1 2 3 4 5 6 | import collections import enum import functools import itertools import typing | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import collections import enum import functools import itertools import typing from PySide6.QtCore import Qt, QSizeF, QPointF, QRectF, Signal, QObject, Slot, \ QPersistentModelIndex, QModelIndex, QRect, QPoint, QSize 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 from mtg_proxy_printer.model.document_loader import PageLayoutSettings from mtg_proxy_printer.units_and_sizes import PageType, unit_registry, RESOLUTION, CardSizes, CardSize, QuantityT |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/printing_filter_widgets.py.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import abc from functools import partial from typing import List, Tuple | | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import abc from functools import partial from typing import List, Tuple 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: from mtg_proxy_printer.ui.generated.settings_window.format_printing_filter import Ui_FormatPrintingFilter from mtg_proxy_printer.ui.generated.settings_window.general_printing_filter import Ui_GeneralPrintingFilter |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/progress_bar.py.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # 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 <http://www.gnu.org/licenses/>. | | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # 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 <http://www.gnu.org/licenses/>. 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 Ui_ProgressBar = load_ui_from_file("progress_bar") |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/settings_window.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pathlib import typing from functools import partial | | | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pathlib import typing from functools import partial 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 from mtg_proxy_printer.document_controller.edit_document_settings import ActionEditDocumentSettings |
︙ | ︙ |
Changes to mtg_proxy_printer/ui/settings_window_pages.py.
︙ | ︙ | |||
15 16 17 18 19 20 21 | import logging from functools import partial import pathlib import typing from abc import abstractmethod | | | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import logging from functools import partial import pathlib import typing from abc import abstractmethod 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 from mtg_proxy_printer.ui.common import highlight_widget from mtg_proxy_printer.units_and_sizes import OptStr, ConfigParser |
︙ | ︙ | |||
65 66 67 68 69 70 71 | class PageMetadata(typing.NamedTuple): text: str icon_name: OptStr tooltip: OptStr = None | < | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 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() item = QStandardItem(data.text) if data.icon_name: |
︙ | ︙ | |||
102 103 104 105 106 107 108 | @abstractmethod def highlight_differing_settings(self, settings: ConfigParser): """Highlights GUI widgets with a state different from the given settings""" pass def clear_highlight(self): """Clears all GUI widget highlights.""" | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | @abstractmethod def highlight_differing_settings(self, settings: ConfigParser): """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 item.setGraphicsEffect(None) class DebugSettingsPage(Page): requested_card_download = Signal(pathlib.Path) |
︙ | ︙ |
Changes to mtg_proxy_printer/units_and_sizes.py.
︙ | ︙ | |||
24 25 26 27 28 29 30 | from typing_extensions import NotRequired 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 | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | from typing_extensions import NotRequired 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 PySide6.QtCore import QSize def _setup_units() -> typing.Tuple[pint.UnitRegistry, QuantityT]: registry = pint.UnitRegistry() resolution = registry.parse_expression("300dots/inch") print_context = pint.Context("print") print_context.add_transformation("[length]", "[printing_unit]", lambda _, x: x*RESOLUTION) |
︙ | ︙ |
Changes to mtg_proxy_printer/update_checker.py.
︙ | ︙ | |||
17 18 19 20 21 22 23 | import re import socket import typing import urllib.parse import urllib.error import ijson | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import re import socket import typing import urllib.parse import urllib.error import ijson 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 from mtg_proxy_printer.card_info_downloader import CardInfoDatabaseImportWorker, CardInfoWorkerBase from mtg_proxy_printer.natsort import natural_sorted, str_less_than |
︙ | ︙ |
Changes to pyproject.toml.
︙ | ︙ | |||
29 30 31 32 33 34 35 | ] # Minimum required ijson version is 3.1 for the use_float parameter. # 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", | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ] # Minimum required ijson version is 3.1 for the use_float parameter. # 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", "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 "delegateto == 1.5", "PyHamcrest >= 2", |
︙ | ︙ | |||
53 54 55 56 57 58 59 | [project.optional-dependencies] dev = [ "pytest", "pytest-cov", "pytest-timeout", "pytest-qt >= 2.0", "tox >= 4.0", | < < < < | < < | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | [project.optional-dependencies] dev = [ "pytest", "pytest-cov", "pytest-timeout", "pytest-qt >= 2.0", "tox >= 4.0", "pillow; platform_system == 'Windows'", # Used to convert the app icon to .ico ] package = [ "build", "cx_Freeze >= 6.11.1; platform.python_implementation == 'CPython'", "pillow; platform_system == 'Windows'", # Used to convert the app icon to .ico ] [project.urls] Homepage = "https://chiselapp.com/user/luziferius/repository/MTGProxyPrinter/index" "Bug tracker" = "https://chiselapp.com/user/luziferius/repository/MTGProxyPrinter/ticket" |
︙ | ︙ | |||
102 103 104 105 106 107 108 | version = {attr= "mtg_proxy_printer.meta_data.__version__"} [build-system] requires = [ "setuptools >=61", "wheel", | | | 96 97 98 99 100 101 102 103 104 105 106 107 | version = {attr= "mtg_proxy_printer.meta_data.__version__"} [build-system] requires = [ "setuptools >=61", "wheel", "PySide6_Essentials >= 6.7.0", "build", "tox >= 4.0", ] build-backend = "setuptools.build_meta" |
Changes to run_tests.bat.
1 2 3 | :: Runs the unit tests :: Create or activate the build environment | | | | 1 2 3 4 5 6 7 8 9 10 | :: Runs the unit tests :: Create or activate the build environment IF EXIST "venv-PySide6" ( call venv-PySide6\Scripts\activate.bat ) ELSE ( call create_development_environment.bat ) tox run |
Changes to run_tests.sh.
1 2 | #!/bin/bash | | | 1 2 3 4 5 | #!/bin/bash source venv-PySide6/bin/activate tox run deactivate |
Changes to scripts/clean_windows_build.bat.
︙ | ︙ | |||
9 10 11 12 13 14 15 | :: 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 <http://www.gnu.org/licenses/>. | < < | | < | < | | < > > > | | | | | < | < < | | < < < < < < < < < < < < < < > | < | | < < < < > > | < < < < < < < < < < | | > | < < < < < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | :: 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 <http://www.gnu.org/licenses/>. 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 |
Changes to scripts/compile_resources.py.
︙ | ︙ | |||
69 70 71 72 73 74 75 | def split_iterable(iterable: typing.Iterable[T], chunk_size: int, /) -> typing.List[typing.Tuple[T, ...]]: """Split the given iterable into chunks of size chunk_size. Does not add padding values to the last item.""" iterable = iter(iterable) return list(iter(lambda: tuple(itertools.islice(iterable, chunk_size)), ())) def compile(): | | < | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | def split_iterable(iterable: typing.Iterable[T], chunk_size: int, /) -> typing.List[typing.Tuple[T, ...]]: """Split the given iterable into chunks of size chunk_size. Does not add padding values to the last item.""" iterable = iter(iterable) return list(iter(lambda: tuple(itertools.islice(iterable, chunk_size)), ())) def compile(): 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) def main(): args = parse_args() |
︙ | ︙ |
Changes to scripts/compile_ui_files.py.
︙ | ︙ | |||
21 22 23 24 25 26 27 | like Python wheels and application bundles created via cx_Freeze. - Creation of type hinting stubs with suffix ".pyi". These are used during development, to provide type hinting and autocompletion for the Ui classes defined by the UI files. """ import argparse import ast | < > < < | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | like Python wheels and application bundles created via cx_Freeze. - Creation of type hinting stubs with suffix ".pyi". These are used during development, to provide type hinting and autocompletion for the Ui classes defined by the UI files. """ import argparse import ast 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 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") ClassRegistry = Dict[str, ast.ImportFrom] UsedClasses = Set[str] |
︙ | ︙ | |||
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | args = parser.parse_args() return args def type_filter(any_: Iterable[Any], types: Union[Type[T], Tuple[Type[T], ...]]) -> Iterable[T]: return filter(lambda x: isinstance(x, types), any_) 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. 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) | > > > > > > | | < | < | < < < < | | < < < < < < < < < < > | < < < > | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | args = parser.parse_args() return args 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. 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) create_python_package(target_path) 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. Recursively finds UI files under source_path, replicates the found directory tree as a Python package hierarchy and 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 create_python_package(parent_dir) (parent_dir/f"{ui_file.stem}.pyi").write_text(stub, "utf-8") 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 = {} for py_file in package_path.rglob("*.py"): module_path = ".".join((py_file.parent.relative_to(package_path.parent) / py_file.stem).parts) root_node = ast.parse(py_file.read_text("utf-8"), py_file) for class_def in type_filter(root_node.body, ast.ClassDef): result[class_def.name] = ast.ImportFrom(module_path, [ast.alias(class_def.name)]) return result def compile_ui_file(path: Path) -> str: try: command = ("pyside6-uic", "--generator", "python", str(path)) except Exception as e: raise RuntimeError(f"Compilation failed for file {path}") from e 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." # Keep all imports unmodified imports = "import typing\n\n" |
︙ | ︙ | |||
211 212 213 214 215 216 217 | return f"class {class_root.name}({base_classes}):" def get_assignments(function_body: List[ast.stmt]) -> List[Assignment]: return [ Assignment( assignment.targets[0].attr, | | < < < < < < < < < | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | return f"class {class_root.name}({base_classes}):" def get_assignments(function_body: List[ast.stmt]) -> List[Assignment]: return [ Assignment( assignment.targets[0].attr, 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_function_stub(function_body: ast.FunctionDef, found_class_uses: UsedClasses): for index, arg in enumerate(function_body.args.args): if arg.arg == "self": continue found_class_uses.add(arg.arg) arg.annotation = ast.Constant(arg.arg) |
︙ | ︙ |
Changes to scripts/update_translations.py.
︙ | ︙ | |||
16 17 18 19 20 21 22 | # along with this program. If not, see <http://www.gnu.org/licenses/>. """ Management script for application translations """ import argparse | < < | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | # along with this program. If not, see <http://www.gnu.org/licenses/>. """ Management script for application translations """ import argparse import pathlib import re import subprocess from typing import Callable, NamedTuple # 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-US": "en_US", "es-ES": "es", "fr-FR": "fr", "it-IT": "it", "ja-JP": "ja", "ko-KR": "ko", "pt-PT": "pt", |
︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # Fetch the name of the sources .ts file from crowdin.yml. # (Since Python does not come with a YAML parser, use a simple RE for data extraction) re.search( r'"source":\s*"(?P<path>.+)",', crowdin_yml_path.read_text("utf-8") )["path"] ) class Namespace(NamedTuple): """Mock namespace for type hinting""" command: Callable[["Namespace"], None] def parse_args() -> Namespace: | > | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | # Fetch the name of the sources .ts file from crowdin.yml. # (Since Python does not come with a YAML parser, use a simple RE for data extraction) re.search( r'"source":\s*"(?P<path>.+)",', crowdin_yml_path.read_text("utf-8") )["path"] ) class Namespace(NamedTuple): """Mock namespace for type hinting""" command: Callable[["Namespace"], None] def parse_args() -> Namespace: |
︙ | ︙ | |||
85 86 87 88 89 90 91 | subprocess.check_output("crowdin") except FileNotFoundError as e: 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) | < < < < < < < < < < < | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | subprocess.check_output("crowdin") except FileNotFoundError as e: 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) 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() verify_crowdin_cli_present() subprocess.call([ |
︙ | ︙ | |||
135 136 137 138 139 140 141 | try: subprocess.check_output(["lrelease", "-version"]) 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 | < | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | try: subprocess.check_output(["lrelease", "-version"]) 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 lrelease6 = venv / "Lib" / "site-packages" / "PySide6" / "lrelease.exe" if not lrelease6.is_file(): raise RuntimeError("No fallback lrelease executable found") return lrelease6 else: return "lrelease" def compile_translations(args: Namespace): """Compiles .ts files into importable, binary translation files with .qm suffix.""" lrelease = get_lrelease() |
︙ | ︙ |
Changes to setup_cx_freeze.py.
︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 | base = "Win32GUI" if sys.platform == "win32" else None excludes = [ f"{main_package}.resources", # Do not include the raw resources as individual files "distutils", "lib2to3", "pep517", | > > | > > < < | < | | < | | | < | < | | | < | < < | < | < < | | | < < | > | | | | | | | < | < | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 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", "pint.testsuite", # Ignore the internal test suite "pydoc_data", "pytest", "sqlite3.test", # Ignore the internal test suite "tkinter", "toml", # 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", "platformdirs.macos", "platformdirs.unix", |
︙ | ︙ |
Changes to tests/conftest.py.
︙ | ︙ | |||
18 19 20 21 22 23 24 | fixtures defined here are available in all test modules. """ import itertools import sqlite3 import unittest.mock from pathlib import Path | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | fixtures defined here are available in all test modules. """ import itertools import sqlite3 import unittest.mock from pathlib import Path 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 from mtg_proxy_printer.model.carddb import CardDatabase from mtg_proxy_printer.model.document import Document |
︙ | ︙ | |||
53 54 55 56 57 58 59 | ":memory:", "document-v6", CardDatabase.MIN_SUPPORTED_SQLITE_VERSION, check_same_thread=False) if request.param: db.execute("PRAGMA reverse_unordered_selects = TRUE") return db @pytest.fixture | | < < < | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | ":memory:", "document-v6", CardDatabase.MIN_SUPPORTED_SQLITE_VERSION, check_same_thread=False) if request.param: db.execute("PRAGMA reverse_unordered_selects = TRUE") return db @pytest.fixture 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 key = ImageKey(scryfall_id, is_front, True) image_db.loaded_images[key] = image_db.blank_image.copy(0, 0, regular_width, regular_height) image_db.images_on_disk.add(key) for scryfall_id in ["650722b4-d72b-4745-a1a5-00a34836282b"]: # Oversized card images 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 @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 @pytest.fixture def document_light(qtbot) -> Document: mock_card_db = unittest.mock.NonCallableMagicMock() mock_image_db = unittest.mock.NonCallableMagicMock(spec=ImageDatabase) mock_image_db.blank_image = QPixmap(CardSizes.REGULAR.as_qsize_px()) mock_image_db.blank_image.fill(QColorConstants.Transparent) 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 |
Changes to tests/document_controller/test_action_move_cards.py.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # along with this program. If not, see <http://www.gnu.org/licenses/>. from functools import partial import pytest from hamcrest import * | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # along with this program. If not, see <http://www.gnu.org/licenses/>. from functools import partial import pytest from hamcrest import * 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 from .helpers import card_container_with, append_new_card_in_page, card_container_with_name |
︙ | ︙ |
Changes to tests/document_controller/test_action_remove_page.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from functools import partial from hamcrest import * | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from functools import partial from hamcrest import * 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 from .helpers import append_new_pages, append_new_card_in_page, card_container_with, verify_page_index_cache_is_valid |
︙ | ︙ |
Changes to tests/test___main__.py.
︙ | ︙ | |||
41 42 43 44 45 46 47 | @pytest.fixture 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, | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | @pytest.fixture 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) as mocks: mocks["configure_root_logger"] = configure_root_logger yield mocks mocks.clear() def test_main_calls_handle_ssl_certificates(main_mocks): mtg_proxy_printer.__main__.main() |
︙ | ︙ | |||
77 78 79 80 81 82 83 | def test_main_configures_logger(main_mocks): mtg_proxy_printer.__main__.main() main_mocks["configure_root_logger"].assert_called_once() def test_main_calls_exec_on_application_instance(main_mocks): mtg_proxy_printer.__main__.main() | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | def test_main_configures_logger(main_mocks): mtg_proxy_printer.__main__.main() 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() 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() app = main_mocks["Application"]() app.enqueue_startup_tasks.assert_called_once() |
︙ | ︙ |
Changes to tests/test_card_list.py.
︙ | ︙ | |||
15 16 17 18 19 20 21 | from collections import Counter import typing from hamcrest import * import pytest from pytestqt.qtbot import QtBot | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | from collections import Counter import typing from hamcrest import * import pytest from pytestqt.qtbot import QtBot 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 OVERSIZED_ID = "650722b4-d72b-4745-a1a5-00a34836282b" |
︙ | ︙ |
Changes to tests/test_carddb.py.
︙ | ︙ | |||
196 197 198 199 200 201 202 | "regular_english_card", "english_double_faced_card", "english_double_faced_art_series_card", "Flowerfoot_Swordmaster_card", "Flowerfoot_Swordmaster_token", ], ) | | < | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | "regular_english_card", "english_double_faced_card", "english_double_faced_art_series_card", "Flowerfoot_Swordmaster_card", "Flowerfoot_Swordmaster_token", ], ) 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 yield CardIdentificationData("en", "Forest"), "en", "Forest" yield CardIdentificationData("de", "Wald"), "de", "Wald" |
︙ | ︙ |
Changes to tests/test_check_card_rendering.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pytest from hamcrest import * from pytestqt.qtbot import QtBot | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pytest from hamcrest import * from pytestqt.qtbot import QtBot 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 @pytest.fixture def blank_image(qtbot) -> QPixmap: |
︙ | ︙ |
Changes to tests/test_document.py.
︙ | ︙ | |||
16 17 18 19 20 21 22 | import copy import dataclasses import pathlib import typing import unittest.mock import textwrap | | | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import copy import dataclasses import pathlib import typing import unittest.mock import textwrap 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 from mtg_proxy_printer.model.card_list import PageColumns |
︙ | ︙ | |||
358 359 360 361 362 363 364 | custom_layout = PageLayoutSettings( page_height=300*mm, page_width=200*mm, 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)) | | < | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | custom_layout = PageLayoutSettings( page_height=300*mm, page_width=200*mm, 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)) return document def test_document_reset_clears_modified_page_layout(qtbot: QtBot, document_custom_layout: Document): default_layout = PageLayoutSettings.create_from_settings() assert_that( document_custom_layout, has_property("page_layout", not_(equal_to(default_layout))) |
︙ | ︙ |
Changes to tests/test_image_db.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. import io | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # 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 <http://www.gnu.org/licenses/>. import io 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 def qpixmap_to_bytes_io(pixmap: QPixmap) -> io.BytesIO: |
︙ | ︙ | |||
51 52 53 54 55 56 57 | image_db.delete_disk_cache_entries([keys[0]]) assert_that((image_db.db_path / keys[0].format_relative_path()).is_file(), is_(False)) 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)) | < | 51 52 53 54 55 56 57 | image_db.delete_disk_cache_entries([keys[0]]) assert_that((image_db.db_path / keys[0].format_relative_path()).is_file(), is_(False)) 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)) |
Changes to tests/test_known_card_image_model.py.
︙ | ︙ | |||
16 17 18 19 20 21 22 | """ Tests the KnownCardImageModel used internally by the CacheCleanupWizard. """ import pathlib import typing | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | """ Tests the KnownCardImageModel used internally by the CacheCleanupWizard. """ import pathlib import typing 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 from mtg_proxy_printer.model.imagedb import ImageDatabase |
︙ | ︙ | |||
48 49 50 51 52 53 54 | front_image = image_db.db_path/"lowres_front"/"b3"/"b3b87bfc-f97f-4734-94f6-e3e2f335fc4d.png" back_image = image_db.db_path/"lowres_back"/"b3"/"b3b87bfc-f97f-4734-94f6-e3e2f335fc4d.png" 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) | < | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | front_image = image_db.db_path/"lowres_front"/"b3"/"b3b87bfc-f97f-4734-94f6-e3e2f335fc4d.png" back_image = image_db.db_path/"lowres_back"/"b3"/"b3b87bfc-f97f-4734-94f6-e3e2f335fc4d.png" 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) @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): model = KnownCardImageModel(environment.card_db) card = environment.card_db.get_card_with_scryfall_id("b3b87bfc-f97f-4734-94f6-e3e2f335fc4d", is_front) |
︙ | ︙ |
Changes to tests/test_page_layout_settings.py.
︙ | ︙ | |||
17 18 19 20 21 22 23 | import mtg_proxy_printer.settings 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 | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import mtg_proxy_printer.settings 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 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 from tests.helpers import quantity_close_to |
︙ | ︙ |
Changes to tests/ui/settings/test_card_filter_widgets.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import typing from unittest.mock import patch | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import typing from unittest.mock import patch from PySide6.QtWidgets import QCheckBox import pytest from hamcrest import * from mtg_proxy_printer.units_and_sizes import SectionProxy import mtg_proxy_printer.settings from mtg_proxy_printer.ui.printing_filter_widgets import AbstractPrintingFilter, GeneralPrintingFilter, \ FormatPrintingFilter |
︙ | ︙ |
Changes to tests/ui/settings/test_initial_page_selection.py.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # 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 <http://www.gnu.org/licenses/>. | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # 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 <http://www.gnu.org/licenses/>. from PySide6.QtCore import QStringListModel from hamcrest import * from pytestqt.qtbot import QtBot from mtg_proxy_printer.ui.settings_window import SettingsWindow |
︙ | ︙ |
Changes to tests/ui/test_add_card.py.
︙ | ︙ | |||
15 16 17 18 19 20 21 | import typing import pytest from hamcrest import * from pytestqt.qtbot import QtBot | | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import typing import pytest from hamcrest import * from pytestqt.qtbot import QtBot 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 StringList = typing.List[str] |
︙ | ︙ | |||
42 43 44 45 46 47 48 | """ fill_card_database_with_json_card(qtbot, card_db, "english_double_faced_art_series_card") 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) | > > > | > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | """ fill_card_database_with_json_card(qtbot, card_db, "english_double_faced_art_series_card") 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.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))) |
Changes to tests/ui/test_card_item.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. from hamcrest import * import pytest | | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # 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 <http://www.gnu.org/licenses/>. from hamcrest import * import pytest 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 |
︙ | ︙ |
Changes to tests/ui/test_central_widget.py.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # along with this program. If not, see <http://www.gnu.org/licenses/>. from pathlib import PurePath from unittest.mock import NonCallableMagicMock, patch import pytest from pytestqt.qtbot import QtBot | | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # along with this program. If not, see <http://www.gnu.org/licenses/>. from pathlib import PurePath from unittest.mock import NonCallableMagicMock, patch import pytest from pytestqt.qtbot import QtBot 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 # Import dynamically used by pytest. Without this, the main_window fixture won’t be found by pytest. |
︙ | ︙ |
Changes to tests/ui/test_deck_import_wizard.py.
︙ | ︙ | |||
18 19 20 21 22 23 24 | import unittest.mock from unittest.mock import MagicMock from hamcrest import * from pytestqt.qtbot import QtBot import pytest | | | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import unittest.mock from unittest.mock import MagicMock from hamcrest import * from pytestqt.qtbot import QtBot import pytest 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, \ GenericRegularExpressionDeckParser from mtg_proxy_printer.model.card_list import PageColumns |
︙ | ︙ |
Changes to tests/ui/test_item_delegate.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from collections import Counter import itertools from unittest.mock import NonCallableMagicMock | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from collections import Counter import itertools from unittest.mock import NonCallableMagicMock 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 from mtg_proxy_printer.model.card_list import CardListModel from mtg_proxy_printer.model.document import Document |
︙ | ︙ |
Changes to tests/ui/test_main_window.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import dataclasses import pathlib import unittest.mock | < | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import dataclasses import pathlib import unittest.mock 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 import mtg_proxy_printer.downloader_base from mtg_proxy_printer.sqlite_helpers import open_database |
︙ | ︙ | |||
58 59 60 61 62 63 64 | cid = CardInfoDownloader(card_db) main_window = MainWindow(card_db, cid, document.image_db, document, QStringListModel(["en"])) qtbot.add_widget(main_window) with qtbot.wait_exposed(main_window, timeout=1000): main_window.show() yield main_window main_window.hide() | < < | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | cid = CardInfoDownloader(card_db) main_window = MainWindow(card_db, cid, document.image_db, document, QStringListModel(["en"])) qtbot.add_widget(main_window) with qtbot.wait_exposed(main_window, timeout=1000): main_window.show() yield main_window main_window.hide() 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 mtg_proxy_printer.downloader_base.mtg_proxy_printer.http_file.MeteredSeekableHTTPFile, "_read_content_length") as cl_mock, \ |
︙ | ︙ | |||
140 141 142 143 144 145 146 | unittest.mock.patch( "mtg_proxy_printer.card_info_downloader.CardInfoDatabaseImportWorker.import_card_data_from_online_api") as import_from_api, \ 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() | < < | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | unittest.mock.patch( "mtg_proxy_printer.card_info_downloader.CardInfoDatabaseImportWorker.import_card_data_from_online_api") as import_from_api, \ 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() 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) with unittest.mock.patch.object( mtg_proxy_printer.ui.main_window.QMessageBox, "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() 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) with unittest.mock.patch.object( mtg_proxy_printer.ui.main_window.QMessageBox, "warning", return_value=StandardButton.Ok |
︙ | ︙ |
Changes to tests/ui/test_page_config_container.py.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # 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 <http://www.gnu.org/licenses/>. | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # 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 <http://www.gnu.org/licenses/>. from PySide6.QtWidgets import QCheckBox, QDoubleSpinBox, QLineEdit import pytest from pytestqt.qtbot import QtBot from hamcrest import * from mtg_proxy_printer.model.document_loader import PageLayoutSettings from mtg_proxy_printer.units_and_sizes import QuantityT, unit_registry |
︙ | ︙ |
Changes to tests/ui/test_page_config_dialog.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. import pytest | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. import pytest from PySide6.QtWidgets import QDialogButtonBox from mtg_proxy_printer.ui.dialogs import DocumentSettingsDialog StandardButton = QDialogButtonBox.StandardButton def test__init__(qtbot, document_light): """Ensure that the dialog can be instantiated""" DocumentSettingsDialog(document_light) |
︙ | ︙ |
Changes to tests/ui/test_page_config_widget.py.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from unittest.mock import patch import pint | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from unittest.mock import patch import pint from PySide6.QtWidgets import QDoubleSpinBox, QCheckBox, QLineEdit from hamcrest import * import pytest from pytestqt.qtbot import QtBot from mtg_proxy_printer.model.document_loader import PageLayoutSettings import mtg_proxy_printer.settings |
︙ | ︙ | |||
118 119 120 121 122 123 124 125 126 127 128 129 130 131 | assert_that(ui, has_property(attribute_name, instance_of(QLineEdit))) assert_that(widget.page_layout, has_property(attribute_name, instance_of(str))) 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", "margin-top": "0 mm", "margin-bottom": "0 mm", | > | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | assert_that(ui, has_property(attribute_name, instance_of(QLineEdit))) assert_that(widget.page_layout, has_property(attribute_name, instance_of(str))) 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", "margin-top": "0 mm", "margin-bottom": "0 mm", |
︙ | ︙ |
Changes to tests/ui/test_page_renderer.py.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from unittest.mock import patch import pytest from hamcrest import * | | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from unittest.mock import patch import pytest from hamcrest import * 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." @pytest.fixture |
︙ | ︙ | |||
50 51 52 53 54 55 56 | @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() | | | 50 51 52 53 54 55 56 57 | @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) |
Changes to tests/ui/test_page_scene.py.
︙ | ︙ | |||
19 20 21 22 23 24 25 | import typing from unittest.mock import patch from math import ceil from hamcrest import * import pytest | | | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import typing from unittest.mock import patch from math import ceil from hamcrest import * import pytest 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 from mtg_proxy_printer.model.document import Document |
︙ | ︙ |
Changes to tests/ui/test_progress_bar.py.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # 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 <http://www.gnu.org/licenses/>. from hamcrest import * import pytest | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # 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 <http://www.gnu.org/licenses/>. from hamcrest import * import pytest 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 INNER_ELEMENTS = ["inner_progress_bar", "inner_progress_label"] |
︙ | ︙ |