2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2015-05-11 16:29:12 +02:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup pythonintern
|
2015-05-11 16:29:12 +02:00
|
|
|
*
|
|
|
|
|
* This file defines a singleton py object accessed via 'bpy.utils.previews',
|
|
|
|
|
* which exposes low-level API for custom previews/icons.
|
|
|
|
|
* It is replaced in final API by an higher-level python wrapper, that handles previews by addon,
|
|
|
|
|
* and automatically release them on deletion.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <Python.h>
|
|
|
|
|
#include <structmember.h>
|
|
|
|
|
|
2023-08-10 22:40:27 +02:00
|
|
|
#include "RNA_access.hh"
|
2024-07-10 18:30:02 +02:00
|
|
|
#include "RNA_prototypes.hh"
|
2015-05-11 16:29:12 +02:00
|
|
|
|
2024-09-24 11:30:38 +02:00
|
|
|
#include "bpy_rna.hh"
|
2024-09-24 12:23:25 +02:00
|
|
|
#include "bpy_utils_previews.hh"
|
2015-05-11 16:29:12 +02:00
|
|
|
|
2024-09-24 15:25:36 +02:00
|
|
|
#include "../generic/py_capi_utils.hh"
|
2023-08-11 12:28:28 +10:00
|
|
|
|
2024-01-18 22:50:23 +02:00
|
|
|
#include "IMB_thumbs.hh"
|
2015-05-11 16:29:12 +02:00
|
|
|
|
2023-09-04 18:02:16 +02:00
|
|
|
#include "BKE_preview_image.hh"
|
2015-05-11 16:29:12 +02:00
|
|
|
|
|
|
|
|
#define STR_SOURCE_TYPES "'IMAGE', 'MOVIE', 'BLEND', 'FONT'"
|
|
|
|
|
|
2020-09-17 12:42:07 +10:00
|
|
|
PyDoc_STRVAR(
|
2024-01-25 10:22:16 +11:00
|
|
|
/* Wrap. */
|
2020-09-17 12:42:07 +10:00
|
|
|
bpy_utils_previews_new_doc,
|
|
|
|
|
".. method:: new(name)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" Generate a new empty preview.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" :arg name: The name (unique id) identifying the preview.\n"
|
2024-11-03 15:42:19 +11:00
|
|
|
" :type name: str\n"
|
2020-09-17 12:42:07 +10:00
|
|
|
" :return: The Preview matching given name, or a new empty one.\n"
|
2020-09-29 15:05:45 -04:00
|
|
|
" :rtype: :class:`bpy.types.ImagePreview`\n"
|
2025-08-22 14:05:28 +10:00
|
|
|
/* This is only true when accessed via `bpy.utils.previews.ImagePreviewCollection.load`,
|
2020-09-29 15:05:45 -04:00
|
|
|
* however this is the public API, allow this minor difference to the internal version here. */
|
2025-08-22 14:05:28 +10:00
|
|
|
" :raises KeyError: if ``name`` already exists.\n");
|
2023-07-21 02:18:59 +02:00
|
|
|
static PyObject *bpy_utils_previews_new(PyObject * /*self*/, PyObject *args)
|
2015-05-11 16:29:12 +02:00
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
PreviewImage *prv;
|
|
|
|
|
|
|
|
|
|
if (!PyArg_ParseTuple(args, "s:new", &name)) {
|
2023-07-21 02:18:59 +02:00
|
|
|
return nullptr;
|
2015-05-11 16:29:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prv = BKE_previewimg_cached_ensure(name);
|
2025-01-24 16:45:32 +01:00
|
|
|
PointerRNA ptr = RNA_pointer_create_discrete(nullptr, &RNA_ImagePreview, prv);
|
2015-05-11 16:29:12 +02:00
|
|
|
|
|
|
|
|
return pyrna_struct_CreatePyObject(&ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyDoc_STRVAR(
|
2024-01-25 10:22:16 +11:00
|
|
|
/* Wrap. */
|
2015-05-11 16:29:12 +02:00
|
|
|
bpy_utils_previews_load_doc,
|
2015-05-12 17:59:37 +10:00
|
|
|
".. method:: load(name, filepath, filetype, force_reload=False)\n"
|
2015-05-11 16:29:12 +02:00
|
|
|
"\n"
|
2020-09-17 12:42:07 +10:00
|
|
|
" Generate a new preview from given file path.\n"
|
|
|
|
|
"\n"
|
2015-05-11 16:29:12 +02:00
|
|
|
" :arg name: The name (unique id) identifying the preview.\n"
|
2024-11-03 15:42:19 +11:00
|
|
|
" :type name: str\n"
|
2015-05-12 17:59:37 +10:00
|
|
|
" :arg filepath: The file path to generate the preview from.\n"
|
2024-11-03 15:42:19 +11:00
|
|
|
" :type filepath: str | bytes\n"
|
2015-05-12 17:59:37 +10:00
|
|
|
" :arg filetype: The type of file, needed to generate the preview in [" STR_SOURCE_TYPES
|
|
|
|
|
"].\n"
|
2024-11-03 15:42:19 +11:00
|
|
|
" :type filetype: str\n"
|
2015-05-11 16:29:12 +02:00
|
|
|
" :arg force_reload: If True, force running thumbnail manager even if preview already "
|
|
|
|
|
"exists in cache.\n"
|
|
|
|
|
" :type force_reload: bool\n"
|
|
|
|
|
" :return: The Preview matching given name, or a new empty one.\n"
|
2020-09-29 15:05:45 -04:00
|
|
|
" :rtype: :class:`bpy.types.ImagePreview`\n"
|
|
|
|
|
/* This is only true when accessed via 'bpy.utils.previews.ImagePreviewCollection.load',
|
|
|
|
|
* however this is the public API, allow this minor difference to the internal version here. */
|
2025-08-22 14:05:28 +10:00
|
|
|
" :raises KeyError: if ``name`` already exists.\n");
|
2023-07-21 02:18:59 +02:00
|
|
|
static PyObject *bpy_utils_previews_load(PyObject * /*self*/, PyObject *args)
|
2015-05-11 16:29:12 +02:00
|
|
|
{
|
2023-08-11 14:59:55 +10:00
|
|
|
char *name;
|
|
|
|
|
PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
|
2023-08-11 12:28:28 +10:00
|
|
|
const PyC_StringEnumItems path_type_items[] = {
|
|
|
|
|
{THB_SOURCE_IMAGE, "IMAGE"},
|
|
|
|
|
{THB_SOURCE_MOVIE, "MOVIE"},
|
|
|
|
|
{THB_SOURCE_BLEND, "BLEND"},
|
|
|
|
|
{THB_SOURCE_FONT, "FONT"},
|
|
|
|
|
{THB_SOURCE_OBJECT_IO, "OBJECT_IO"},
|
|
|
|
|
{0, nullptr},
|
|
|
|
|
};
|
|
|
|
|
PyC_StringEnum path_type = {
|
|
|
|
|
path_type_items,
|
|
|
|
|
/* The default isn't used. */
|
|
|
|
|
0,
|
|
|
|
|
};
|
|
|
|
|
int force_reload = false;
|
|
|
|
|
|
|
|
|
|
if (!PyArg_ParseTuple(args,
|
|
|
|
|
"s" /* `name` */
|
2023-08-11 14:59:55 +10:00
|
|
|
"O&" /* `filepath` */
|
2023-08-11 12:28:28 +10:00
|
|
|
"O&" /* `filetype` */
|
|
|
|
|
"|" /* Optional arguments. */
|
|
|
|
|
"p" /* `force_reload` */
|
|
|
|
|
":load",
|
|
|
|
|
&name,
|
2023-08-11 14:59:55 +10:00
|
|
|
PyC_ParseUnicodeAsBytesAndSize,
|
|
|
|
|
&filepath_data,
|
2023-08-11 12:28:28 +10:00
|
|
|
PyC_ParseStringEnum,
|
|
|
|
|
&path_type,
|
|
|
|
|
&force_reload))
|
|
|
|
|
{
|
2023-07-21 02:18:59 +02:00
|
|
|
return nullptr;
|
2015-05-11 16:29:12 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-08-11 12:28:28 +10:00
|
|
|
PreviewImage *prv = BKE_previewimg_cached_thumbnail_read(
|
2023-08-11 14:59:55 +10:00
|
|
|
name, filepath_data.value, path_type.value_found, force_reload);
|
|
|
|
|
|
|
|
|
|
Py_XDECREF(filepath_data.value_coerce);
|
|
|
|
|
|
2025-01-24 16:45:32 +01:00
|
|
|
PointerRNA ptr = RNA_pointer_create_discrete(nullptr, &RNA_ImagePreview, prv);
|
2015-05-11 16:29:12 +02:00
|
|
|
return pyrna_struct_CreatePyObject(&ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-25 10:22:16 +11:00
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_utils_previews_release_doc,
|
|
|
|
|
".. method:: release(name)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" Release (free) a previously created preview.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" :arg name: The name (unique id) identifying the preview.\n"
|
2024-11-03 15:42:19 +11:00
|
|
|
" :type name: str\n");
|
2023-07-21 02:18:59 +02:00
|
|
|
static PyObject *bpy_utils_previews_release(PyObject * /*self*/, PyObject *args)
|
2015-05-11 16:29:12 +02:00
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
|
|
if (!PyArg_ParseTuple(args, "s:release", &name)) {
|
2023-07-21 02:18:59 +02:00
|
|
|
return nullptr;
|
2015-05-11 16:29:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_previewimg_cached_release(name);
|
|
|
|
|
|
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-01 12:06:03 +11:00
|
|
|
#ifdef __GNUC__
|
|
|
|
|
# ifdef __clang__
|
|
|
|
|
# pragma clang diagnostic push
|
|
|
|
|
# pragma clang diagnostic ignored "-Wcast-function-type"
|
|
|
|
|
# else
|
|
|
|
|
# pragma GCC diagnostic push
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2023-06-03 08:36:28 +10:00
|
|
|
static PyMethodDef bpy_utils_previews_methods[] = {
|
2015-05-11 16:29:12 +02:00
|
|
|
/* Can't use METH_KEYWORDS alone, see http://bugs.python.org/issue11587 */
|
|
|
|
|
{"new", (PyCFunction)bpy_utils_previews_new, METH_VARARGS, bpy_utils_previews_new_doc},
|
|
|
|
|
{"load", (PyCFunction)bpy_utils_previews_load, METH_VARARGS, bpy_utils_previews_load_doc},
|
|
|
|
|
{"release",
|
|
|
|
|
(PyCFunction)bpy_utils_previews_release,
|
|
|
|
|
METH_VARARGS,
|
|
|
|
|
bpy_utils_previews_release_doc},
|
2023-07-21 02:18:59 +02:00
|
|
|
{nullptr, nullptr, 0, nullptr},
|
2015-05-11 16:29:12 +02:00
|
|
|
};
|
|
|
|
|
|
2025-04-01 12:06:03 +11:00
|
|
|
#ifdef __GNUC__
|
|
|
|
|
# ifdef __clang__
|
|
|
|
|
# pragma clang diagnostic pop
|
|
|
|
|
# else
|
|
|
|
|
# pragma GCC diagnostic pop
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-05-11 16:29:12 +02:00
|
|
|
PyDoc_STRVAR(
|
2024-01-25 10:22:16 +11:00
|
|
|
/* Wrap. */
|
2015-05-11 16:29:12 +02:00
|
|
|
bpy_utils_previews_doc,
|
|
|
|
|
"This object contains basic static methods to handle cached (non-ID) previews in Blender\n"
|
2025-08-22 14:05:28 +10:00
|
|
|
"(low-level API, not exposed to final users).\n");
|
2023-06-03 08:36:28 +10:00
|
|
|
static PyModuleDef bpy_utils_previews_module = {
|
2023-07-16 17:43:31 +10:00
|
|
|
/*m_base*/ PyModuleDef_HEAD_INIT,
|
2022-11-08 11:13:58 +11:00
|
|
|
/*m_name*/ "bpy._utils_previews",
|
|
|
|
|
/*m_doc*/ bpy_utils_previews_doc,
|
|
|
|
|
/*m_size*/ 0,
|
|
|
|
|
/*m_methods*/ bpy_utils_previews_methods,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*m_slots*/ nullptr,
|
|
|
|
|
/*m_traverse*/ nullptr,
|
|
|
|
|
/*m_clear*/ nullptr,
|
|
|
|
|
/*m_free*/ nullptr,
|
2015-05-11 16:29:12 +02:00
|
|
|
};
|
|
|
|
|
|
2023-07-21 10:59:54 +10:00
|
|
|
PyObject *BPY_utils_previews_module()
|
2015-05-11 16:29:12 +02:00
|
|
|
{
|
|
|
|
|
PyObject *submodule;
|
|
|
|
|
|
|
|
|
|
submodule = PyModule_Create(&bpy_utils_previews_module);
|
|
|
|
|
|
|
|
|
|
return submodule;
|
|
|
|
|
}
|