Coverage for paintmaskanimator/replacement_sets.py: 96%
41 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"""使用色の色置換セットの保存と読み込み(Issue #42)。
3シーン色への置き換えなど、カットをまたいで繰り返し使う色置換がある。毎回
4登録し直さずに済むよう、「元の色 → 置き換える色」の組をファイル(``.pmarep``)
5へ出し入れする。Qt に依存しない素の辞書として扱い、UI は
6``main_window_used_color`` 側が受け持つ。
7"""
8import json
9from pathlib import Path
11#: ファイルの拡張子とダイアログのフィルター。
12SUFFIX = ".pmarep"
13FILE_FILTER = "Paint Mask Replacements (*.pmarep)"
14FORMAT_VERSION = 1
17def _hex(rgb):
18 red, green, blue = (max(0, min(255, int(value))) for value in rgb[:3])
19 return f"#{red:02x}{green:02x}{blue:02x}"
22def _rgb(value):
23 text = str(value).strip().lstrip("#")
24 if len(text) != 6:
25 raise ValueError(value)
26 return tuple(int(text[index:index + 2], 16) for index in (0, 2, 4))
29def to_data(mapping):
30 """``{元の RGB: 置換後の RGB}`` を、書き出せる素のデータにする。"""
31 return {
32 "format": "paintmaskanimator.replacements",
33 "version": FORMAT_VERSION,
34 "replacements": [
35 {"from": _hex(source), "to": _hex(destination)}
36 for source, destination in mapping.items()
37 ],
38 }
41def from_data(data):
42 """読み込んだデータを ``{元の RGB: 置換後の RGB}`` にする。
44 読めない組は飛ばす。手で書き換えたファイルや、将来の項目が増えた
45 ファイルでも、分かるところだけ使えるようにするため。
46 """
47 if not isinstance(data, dict): 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true
48 raise ValueError("replacement set must be an object")
49 entries = data.get("replacements")
50 if not isinstance(entries, list):
51 raise ValueError("replacement set has no replacements")
52 mapping = {}
53 for entry in entries:
54 if not isinstance(entry, dict):
55 continue
56 try:
57 source = _rgb(entry["from"])
58 destination = _rgb(entry["to"])
59 except (KeyError, TypeError, ValueError):
60 continue
61 if source != destination:
62 mapping[source] = destination
63 return mapping
66def save(path, mapping):
67 path = Path(path)
68 if path.suffix.lower() != SUFFIX:
69 path = path.with_suffix(SUFFIX)
70 path.write_text(
71 json.dumps(to_data(mapping), ensure_ascii=False, indent=2),
72 encoding="utf-8",
73 )
74 return path
77def load(path):
78 return from_data(json.loads(Path(path).read_text(encoding="utf-8")))