Coverage for paintmaskanimator/constants.py: 82%

49 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-11-02 04:50 +0000

1"""Application-wide constants. 

2 

3Note: ``CANVAS_WIDTH`` / ``CANVAS_HEIGHT`` are *mutable* runtime state — they 

4change when the project canvas is resized. They are deliberately excluded from 

5``__all__`` so that ``from .constants import *`` does NOT create per-module 

6copies that would go stale after a resize. Always read/write them through the 

7module, e.g. ``constants.CANVAS_WIDTH``. 

8""" 

9 

10import sys 

11from importlib.metadata import PackageNotFoundError, version 

12from pathlib import Path 

13 

14try: 

15 import tomllib 

16except ModuleNotFoundError: 

17 # tomllib is stdlib only from 3.11, and requires-python allows 3.10. It is 

18 # needed solely to read the version out of pyproject.toml in an 

19 # *uninstalled* source checkout, so importing the package must not depend 

20 # on it -- that path just degrades to "development" below. 

21 tomllib = None 

22 

23 

24APP_NAME = "PaintMaskAnimator" 

25 

26 

27def _application_version() -> str: 

28 """Read the canonical version, including from a frozen application. 

29 

30 The build bundles ``pyproject.toml`` beside the package. Prefer it over 

31 distribution metadata so a stale editable-install ``egg-info`` directory 

32 can never leak the previous release number into the UI or updater. 

33 """ 

34 if tomllib is not None: 34 ↛ 42line 34 didn't jump to line 42 because the condition on line 34 was always true

35 project_file = Path(__file__).resolve().parent.parent / "pyproject.toml" 

36 try: 

37 project = tomllib.loads(project_file.read_text(encoding="utf-8")) 

38 return str(project["project"]["version"]) 

39 except (OSError, KeyError, tomllib.TOMLDecodeError): 

40 pass 

41 

42 try: 

43 return version("paintmaskanimator") 

44 except PackageNotFoundError: 

45 return "development" 

46 

47 

48APP_VERSION = _application_version() 

49APP_DISPLAY_NAME = f"{APP_NAME} V{APP_VERSION}" 

50# GitHub repository: releases are distributed from here, and the in-app 

51# updater reads the latest one through the public REST API (no credentials). 

52GITHUB_REPO = "SehataKuro/PaintMaskAnimator" 

53RELEASES_URL = f"https://github.com/{GITHUB_REPO}/releases" 

54UPDATE_RELEASE_API_URL = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest" 

55# 「拡大縮小(押している間)」の既定キー。macOSのQtでは "Ctrl" が⌘になるが、 

56# ⌘SpaceはSpotlight、⌃Space("Meta")は入力ソース切り替えにOSが既定で使って 

57# いて届かないので、⌥Spaceにする。WindowsのAlt+Spaceはウィンドウメニュー。 

58HOLD_ZOOM_SHORTCUT = "Alt+Space" if sys.platform == "darwin" else "Ctrl+Space" 

59# 操作説明に出す修飾キー名。macOSのQtでは Ctrl 扱いのキーは⌘、Alt は⌥で、 

60# 物理的なControl+クリックは右クリックになってしまう。 

61CTRL_KEY_LABEL = "⌘" if sys.platform == "darwin" else "Ctrl" 

62ALT_KEY_LABEL = "⌥" if sys.platform == "darwin" else "Alt" 

63HOLD_ZOOM_KEY_LABEL = f"{ALT_KEY_LABEL if sys.platform == 'darwin' else CTRL_KEY_LABEL}+Space" 

64CANVAS_WIDTH = 1280 

65CANVAS_HEIGHT = 720 

66OUTSIDE_MARGIN = 0 

67MAX_UNDO = 30 

68# Entry count alone does not bound undo memory: 30 full-layer snapshots of a 

69# 4K canvas across several layers is multiple gigabytes, while 30 stroke tile 

70# patches are a few megabytes. The stack is trimmed by whichever limit binds 

71# first, and always keeps at least one entry so undo never becomes a no-op. 

72MAX_UNDO_BYTES = 768 * 1024 * 1024 

73MAX_PROJECT_METADATA_BYTES = 16 * 1024 * 1024 

74MAX_PROJECT_FRAMES = 10000 

75MAX_PROJECT_LAYERS = 256 

76MAX_PROJECT_LAYER_CELLS = 100000 

77MAX_PROJECT_ARCHIVE_BYTES = 8 * 1024 * 1024 * 1024 

78MAX_PROJECT_IMAGE_BYTES = 512 * 1024 * 1024 

79MAX_PROJECT_DECODED_PIXELS = 2 * 1024 * 1024 * 1024 

80MAX_IMAGE_DIMENSION = 16384 

81MAX_SINGLE_IMAGE_PIXELS = MAX_IMAGE_DIMENSION * MAX_IMAGE_DIMENSION 

82TP_MASK_PROXY_THRESHOLD = 5000 

83TP_MASK_PROXY_MAX_DIMENSION = 1280 

84TP_MASK_PROXY_MAX_COLORS = 32 

85# Exact colors the quality transform can carry through untouched. Colors are 

86# one label id per pixel rather than one mask each, so the ceiling is about the 

87# source still being flat-colored art, not about memory. 

88TP_MASK_MAX_TRANSFORM_COLORS = 4096 

89# Coverage probes per axis while dragging the low-resolution proxy: 2x2 keeps 

90# the preview responsive, the committed render uses the full grid. 

91TP_MASK_PROXY_SUBSAMPLES = 2 

92 

93# Everything except the mutable canvas dimensions is safe to star-import. 

94__all__ = [ # pyright: ignore[reportUnsupportedDunderAll] 

95 _n for _n in list(globals()) 

96 if _n.isupper() and _n not in ("CANVAS_WIDTH", "CANVAS_HEIGHT") 

97]