29 const int pointers_num,
34#define FILEPATH_SAVE_ARG \
35 "Accepts one argument: " \
36 "the file being saved, an empty string for the startup-file."
37#define FILEPATH_LOAD_ARG \
38 "Accepts one argument: " \
39 "the file being loaded, an empty string for the startup-file."
40#define RENDER_STATS_ARG \
41 "Accepts one argument: " \
42 "the render stats (render/saving time plus in background mode frame/used [peak] memory)."
43#define DEPSGRAPH_UPDATE_ARG \
44 "Accepts two arguments: " \
45 "The scene data-block and the dependency graph being updated"
47 "Accepts one argument: " \
48 "the scene data-block being rendered"
49#define OBJECT_BAKE_ARG \
50 "Accepts one argument: " \
51 "the object data-block being baked"
52#define COMPOSITE_ARG \
53 "Accepts one argument: " \
54 "the scene data-block"
55#define ANNOTATION_ARG \
56 "Accepts two arguments: " \
57 "the annotation data-block and dependency graph"
58#define BLENDIMPORT_ARG \
59 "Accepts one argument: " \
60 "a BlendImportContext"
67 "Called after frame change for playback and rendering, before any data is evaluated for the "
68 "new frame. This makes it possible to change data and relations (for example swap an object "
69 "to another mesh) for the new frame. Note that this handler is **not** to be used as 'before "
70 "the frame changes' event. The dependency graph is not available in this handler, as data "
71 "and relations may have been altered and the dependency graph has not yet been updated for "
74 "Called after frame change for playback and rendering, after the data has been evaluated "
76 {
"render_pre",
"on render (before)"},
77 {
"render_post",
"on render (after)"},
78 {
"render_write",
"on writing a render frame (directly after the frame is written)"},
80 {
"render_init",
"on initialization of a render job. " RENDER_ARG},
81 {
"render_complete",
"on completion of render job. " RENDER_ARG},
82 {
"render_cancel",
"on canceling a render job. " RENDER_ARG},
86 {
"load_post_fail",
"on failure to load a new blend file (after). " FILEPATH_LOAD_ARG},
90 {
"save_post_fail",
"on failure to save a blend file (after). " FILEPATH_SAVE_ARG},
92 {
"undo_pre",
"on loading an undo step (before)"},
93 {
"undo_post",
"on loading an undo step (after)"},
94 {
"redo_pre",
"on loading a redo step (before)"},
95 {
"redo_post",
"on loading a redo step (after)"},
98 {
"version_update",
"on ending the versioning code"},
99 {
"load_factory_preferences_post",
"on loading factory preferences (after)"},
100 {
"load_factory_startup_post",
"on loading factory startup (after)"},
101 {
"xr_session_start_pre",
"on starting an xr session (before)"},
102 {
"annotation_pre",
"on drawing an annotation (before). " ANNOTATION_ARG},
103 {
"annotation_post",
"on drawing an annotation (after). " ANNOTATION_ARG},
105 {
"object_bake_complete",
106 "on completing a bake job; will be called in the main thread. " OBJECT_BAKE_ARG},
107 {
"object_bake_cancel",
108 "on canceling a bake job; will be called in the main thread. " OBJECT_BAKE_ARG},
109 {
"composite_pre",
"on a compositing background job (before). " COMPOSITE_ARG},
110 {
"composite_post",
"on a compositing background job (after). " COMPOSITE_ARG},
111 {
"composite_cancel",
"on a compositing background job (cancel). " COMPOSITE_ARG},
114 {
"translation_update_post",
"on translation settings update"},
118 {
"_extension_repos_update_pre",
"on changes to extension repos (before)"},
119 {
"_extension_repos_update_post",
"on changes to extension repos (after)"},
120 {
"_extension_repos_sync",
"on creating or synchronizing the active repository"},
121 {
"_extension_repos_files_clear",
122 "remove files from the repository directory (uses as a string argument)"},
123 {
"blend_import_pre",
"on linking or appending data (before). " BLENDIMPORT_ARG},
124 {
"blend_import_post",
"on linking or appending data (after). " BLENDIMPORT_ARG},
127#define APP_CB_OTHER_FIELDS 1
129 "Function decorator for callback functions not to be removed when loading new files"},
136 "This module contains callback lists",
142# if (BKE_CB_EVT_TOT != ARRAY_SIZE(app_cb_info_fields))
143# error "Callbacks are out of sync"
151#define PERMINENT_CB_ID "_bpy_persistent"
159 if (!PyArg_ParseTuple(args,
"O:bpy.app.handlers.persistent", &value)) {
163 if (PyFunction_Check(value)) {
164 PyObject **dict_ptr = _PyObject_GetDictPtr(value);
165 if (dict_ptr ==
nullptr) {
166 PyErr_SetString(PyExc_ValueError,
167 "bpy.app.handlers.persistent wasn't able to "
168 "get the dictionary from the function passed");
173 if (*dict_ptr ==
nullptr) {
174 *dict_ptr = PyDict_New();
183 PyErr_SetString(PyExc_ValueError,
"bpy.app.handlers.persistent expected a function");
190 PyVarObject_HEAD_INIT(
nullptr, 0)
192 PyVarObject_HEAD_INIT(&PyType_Type, 0)
212 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
250 PyObject *app_cb_info;
254 if (app_cb_info ==
nullptr) {
260 Py_FatalError(
"invalid callback slots 1");
265 Py_FatalError(
"invalid callback slots 2");
283 BLI_assert_msg(0,
"error initializing 'bpy.app.handlers.persistent'");
303 funcstore = &funcstore_array[
pos];
305 funcstore->
alloc = 0;
316 PyGILState_STATE gilstate = PyGILState_Ensure();
334 for (
i = PyList_GET_SIZE(ls) - 1;
i >= 0;
i--) {
335 PyObject *item = PyList_GET_ITEM(ls,
i);
337 if (PyMethod_Check(item)) {
338 PyObject *item_test = PyMethod_GET_FUNCTION(item);
345 if (PyFunction_Check(item) && (dict_ptr = _PyObject_GetDictPtr(item)) && (*dict_ptr) &&
346 (PyDict_GetItem(*dict_ptr, perm_id_str) !=
nullptr))
353 PyList_SetSlice(ls,
i,
i + 1,
nullptr);
361 PyGILState_Release(gilstate);
364static PyObject *
choose_arguments(PyObject *func, PyObject *args_all, PyObject *args_single)
366 if (!PyFunction_Check(func)) {
369 PyCodeObject *code = (PyCodeObject *)PyFunction_GetCode(func);
370 if (code->co_argcount == 1) {
379 const int pointers_num,
383 if (PyList_GET_SIZE(cb_list) > 0) {
384 const PyGILState_STATE gilstate = PyGILState_Ensure();
386 const int num_arguments = 2;
387 PyObject *args_all = PyTuple_New(num_arguments);
388 PyObject *args_single = PyTuple_New(1);
394 for (
int i = 0;
i < pointers_num; ++
i) {
398 for (
int i = pointers_num;
i < num_arguments; ++
i) {
399 PyTuple_SET_ITEM(args_all,
i, Py_NewRef(Py_None));
402 if (pointers_num == 0) {
403 PyTuple_SET_ITEM(args_single, 0, Py_NewRef(Py_None));
412 for (
pos = 0;
pos < PyList_GET_SIZE(cb_list);
pos++) {
413 func = PyList_GET_ITEM(cb_list,
pos);
415 ret = PyObject_Call(func, args,
nullptr);
416 if (
ret ==
nullptr) {
424 PySys_WriteStderr(
"Error in bpy.app.handlers.%s[%d]:\n",
437 PyGILState_Release(gilstate);
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt)
#define BLI_assert_msg(a, msg)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
PyObject * BPY_app_handlers_struct()
static PyStructSequence_Desc app_cb_info_desc
#define FILEPATH_SAVE_ARG
#define APP_CB_OTHER_FIELDS
static PyTypeObject BPyPersistent_Type
#define FILEPATH_LOAD_ARG
#define DEPSGRAPH_UPDATE_ARG
static PyObject * make_app_cb_info()
void BPY_app_handlers_reset(const bool do_all)
static PyObject * bpy_app_handlers_persistent_new(PyTypeObject *, PyObject *args, PyObject *)
static PyStructSequence_Field app_cb_info_fields[]
static PyObject * py_cb_array[BKE_CB_EVT_TOT]
static PyTypeObject BlenderAppCbType
static PyObject * choose_arguments(PyObject *func, PyObject *args_all, PyObject *args_single)
void bpy_app_generic_callback(Main *main, PointerRNA **pointers, const int pointers_num, void *arg)
PyObject * pyrna_struct_CreatePyObject_with_primitive_support(PointerRNA *ptr)
header-only compatibility defines.
void(* func)(Main *, PointerRNA **, int num_pointers, void *arg)