Cleanup: use f-string
[blender.git] / release / scripts / modules / bpy / utils / previews.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 """
22 This module contains utility functions to handle custom previews.
23
24 It behaves as a high-level 'cached' previews manager.
25
26 This allows scripts to generate their own previews, and use them as icons in UI widgets
27 ('icon_value' for UILayout functions).
28
29
30 Custom Icon Example
31 -------------------
32
33 .. literalinclude:: __/__/__/release/scripts/templates_py/ui_previews_custom_icon.py
34 """
35
36 __all__ = (
37     "new",
38     "remove",
39     "ImagePreviewCollection",
40 )
41
42 import _bpy
43 _utils_previews = _bpy._utils_previews
44 del _bpy
45
46
47 _uuid_open = set()
48
49
50 # High-level previews manager.
51 # not accessed directly
52 class ImagePreviewCollection(dict):
53     """
54     Dictionary-like class of previews.
55
56     This is a subclass of Python's built-in dict type,
57     used to store multiple image previews.
58
59     .. note::
60
61         - instance with :mod:`bpy.utils.previews.new`
62         - keys must be ``str`` type.
63         - values will be :class:`bpy.types.ImagePreview`
64     """
65
66     # Internal notes:
67     # - Blender's internal 'PreviewImage' struct uses 'self._uuid' prefix.
68
69     def __init__(self):
70         super().__init__()
71         self._uuid = hex(id(self))
72         _uuid_open.add(self._uuid)
73
74     def __del__(self):
75         if self._uuid not in _uuid_open:
76             return
77
78         raise ResourceWarning(
79             f"{self!r}: left open, remove with "
80             "'bpy.utils.previews.remove()'"
81         )
82         self.close()
83
84     def _gen_key(self, name):
85         return ":".join((self._uuid, name))
86
87     def new(self, name):
88         if name in self:
89             raise KeyError(f"key {name!r} already exists")
90         p = self[name] = _utils_previews.new(
91             self._gen_key(name))
92         return p
93     new.__doc__ = _utils_previews.new.__doc__
94
95     def load(self, name, path, path_type, force_reload=False):
96         if name in self:
97             raise KeyError("key {name!r} already exists")
98         p = self[name] = _utils_previews.load(
99             self._gen_key(name), path, path_type, force_reload)
100         return p
101     load.__doc__ = _utils_previews.load.__doc__
102
103     def clear(self):
104         """Clear all previews."""
105         for name in self.keys():
106             _utils_previews.release(self._gen_key(name))
107         super().clear()
108
109     def close(self):
110         """Close the collection and clear all previews."""
111         self.clear()
112         _uuid_open.remove(self._uuid)
113
114     def __delitem__(self, key):
115         _utils_previews.release(self._gen_key(key))
116         super().__delitem__(key)
117
118     def __repr__(self):
119         return f"<{self.__class__.__name__:s} id={self._uuid:s}[{len(self):d}], {super()!r}>"
120
121
122 def new():
123     """
124     :return: a new preview collection.
125     :rtype: :class:`ImagePreviewCollection`
126     """
127
128     return ImagePreviewCollection()
129
130
131 def remove(pcoll):
132     """
133     Remove the specified previews collection.
134
135     :arg pcoll: Preview collection to close.
136     :type pcoll: :class:`ImagePreviewCollection`
137     """
138     pcoll.close()
139
140
141 # don't complain about resources on exit (only unregister)
142 import atexit
143
144
145 def exit_clear_warning():
146     del ImagePreviewCollection.__del__
147
148
149 atexit.register(exit_clear_warning)
150 del atexit, exit_clear_warning