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

1"""Layout-paper images registered in Preferences and offered by File › New. 

2 

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 

10 

11from PySide6.QtGui import QImage, QImageReader 

12 

13from . import config 

14from .i18n import tr 

15 

16PAPERS_KEY = "layout_papers" 

17LAST_USED_KEY = "layout_paper_last" 

18SUFFIXES = (".png", ".jpg", ".jpeg", ".tga") 

19 

20 

21def papers_dir(): 

22 path = config.config_dir() / "layout_papers" 

23 path.mkdir(parents=True, exist_ok=True) 

24 return path 

25 

26 

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 ] 

32 

33 

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()] 

37 

38 

39def paper(paper_id): 

40 return next((e for e in papers() if e["id"] == paper_id), None) 

41 

42 

43def image_path(entry): 

44 return papers_dir() / Path(str(entry["file"])).name 

45 

46 

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() 

57 

58 

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 ) 

66 

67 

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 

79 

80 

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) 

90 

91 

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]) 

98 

99 

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 

104 

105 

106def set_last_used(paper_id): 

107 config.set_value(LAST_USED_KEY, paper_id) 

108 

109 

110def layer_name(): 

111 return tr("レイアウト用紙")