Coverage for paintmaskanimator/layout_paper.py: 91%
66 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 04:50 +0000
1"""Layout-paper images registered in Preferences and offered by File › New.
3A registered image is copied into the config directory, so it keeps working
4after the original is moved or deleted. ``File › New…`` can put the chosen
5one into the new document as the bottom draft layer, and size the canvas to it.
6"""
7import shutil
8import uuid
9from pathlib import Path
11from PySide6.QtGui import QImage, QImageReader
13from . import config
14from .i18n import tr
16PAPERS_KEY = "layout_papers"
17LAST_USED_KEY = "layout_paper_last"
18SUFFIXES = (".png", ".jpg", ".jpeg", ".tga")
21def papers_dir():
22 path = config.config_dir() / "layout_papers"
23 path.mkdir(parents=True, exist_ok=True)
24 return path
27def _entries():
28 return [
29 e for e in (config.get_value(PAPERS_KEY, []) or [])
30 if isinstance(e, dict) and e.get("id") and e.get("file")
31 ]
34def papers():
35 """Registered papers whose image file still exists: ``[{id, name, file}]``."""
36 return [e for e in _entries() if image_path(e).is_file()]
39def paper(paper_id):
40 return next((e for e in papers() if e["id"] == paper_id), None)
43def image_path(entry):
44 return papers_dir() / Path(str(entry["file"])).name
47def image_size(entry):
48 """``(width, height)`` of a paper's image, or ``None`` if unreadable."""
49 reader = QImageReader(str(image_path(entry)))
50 reader.setAutoTransform(True)
51 size = reader.size()
52 if not size.isValid(): 52 ↛ 54line 52 didn't jump to line 54 because the condition on line 52 was never true
53 # Some formats only report their size once decoded.
54 image = load_image(entry)
55 return None if image is None else (image.width(), image.height())
56 return size.width(), size.height()
59def load_image(entry):
60 reader = QImageReader(str(image_path(entry)))
61 reader.setAutoTransform(True)
62 image = reader.read()
63 return None if image.isNull() else image.convertToFormat(
64 QImage.Format.Format_ARGB32_Premultiplied
65 )
68def add_paper(source, name=None):
69 """Copy ``source`` into the paper store; return the new entry or ``None``."""
70 source = Path(source)
71 if source.suffix.lower() not in SUFFIXES or QImageReader(str(source)).read().isNull():
72 return None
73 paper_id = uuid.uuid4().hex
74 target = papers_dir() / f"{paper_id}{source.suffix.lower()}"
75 shutil.copyfile(source, target)
76 entry = {"id": paper_id, "name": str(name or source.stem), "file": target.name}
77 config.set_value(PAPERS_KEY, _entries() + [entry])
78 return entry
81def rename_paper(paper_id, name):
82 name = str(name).strip()
83 if not name: 83 ↛ 84line 83 didn't jump to line 84 because the condition on line 83 was never true
84 return
85 entries = _entries()
86 for entry in entries:
87 if entry["id"] == paper_id: 87 ↛ 86line 87 didn't jump to line 86 because the condition on line 87 was always true
88 entry["name"] = name
89 config.set_value(PAPERS_KEY, entries)
92def remove_paper(paper_id):
93 entries = _entries()
94 for entry in entries:
95 if entry["id"] == paper_id: 95 ↛ 94line 95 didn't jump to line 94 because the condition on line 95 was always true
96 image_path(entry).unlink(missing_ok=True)
97 config.set_value(PAPERS_KEY, [e for e in entries if e["id"] != paper_id])
100def last_used():
101 """The paper chosen in the previous File › New, if it still exists."""
102 paper_id = config.get_value(LAST_USED_KEY)
103 return paper_id if paper(paper_id) is not None else None
106def set_last_used(paper_id):
107 config.set_value(LAST_USED_KEY, paper_id)
110def layer_name():
111 return tr("レイアウト用紙")