diff options
author | Campbell Barton <ideasman42@gmail.com> | 2011-11-03 10:53:52 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2011-11-03 10:53:52 +0400 |
commit | e2393d11090765caf987e5ef8e2e688d80c0e64d (patch) | |
tree | 2aa909746eb9c11fb47c60740f80fb197384565b /source/blender/python | |
parent | 7d7977658ad12a940597da5b78ce70e4e5081f4b (diff) |
ability to have permanent callbacks that stay active when new blend files are loaded.
this works by tagging functions, eg:
def my_func(scene):
pass
bpy.app.handlers.permanent_tag(my_func, True) # <-- important bit
bpy.app.handlers.frame_change_pre.append(my_func)
Diffstat (limited to 'source/blender/python')
-rw-r--r-- | source/blender/python/BPY_extern.h | 2 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app_handlers.c | 113 |
2 files changed, 111 insertions, 4 deletions
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index d8ddcd593a8..29e0185c2f2 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -71,7 +71,7 @@ void BPY_text_free_code(struct Text *text); void BPY_modules_update(struct bContext *C); // XXX - annoying, need this for pointers that get out of date void BPY_modules_load_user(struct bContext *C); -void BPY_app_handlers_reset(void); +void BPY_app_handlers_reset(const short do_all); void BPY_driver_reset(void); float BPY_driver_exec(struct ChannelDriver *driver); diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index e7f74569f1a..9ca0e71e71d 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -49,6 +49,11 @@ static PyStructSequence_Field app_cb_info_fields[]= { {(char *)"save_post", NULL}, {(char *)"scene_update_pre", NULL}, {(char *)"scene_update_post", NULL}, + + /* sets the permanent tag */ +# define APP_CB_OTHER_FIELDS 1 + {(char *)"permanent_tag", NULL}, + {NULL} }; @@ -65,6 +70,71 @@ static PyStructSequence_Desc app_cb_info_desc= { #endif */ +/* --------------------------------------------------------------------------*/ +/* permanent tagging code */ +#define PERMINENT_CB_ID "_bpy_permanent_tag" + +PyDoc_STRVAR(bpy_app_handlers_permanent_tag_doc, +".. function:: permanent_tag(func, state=True)\n" +"\n" +" Set the function as being permanent so its not cleared when new blend files are loaded.\n" +"\n" +" :arg func: The function to set as permanent.\n" +" :type func: function\n" +" :arg state: Set the permanent state to True or False.\n" +" :type state: bool\n" +" :return: the function argument\n" +" :rtype: function\n" +); + +static PyObject *bpy_app_handlers_permanent_tag(PyObject *UNUSED(self), PyObject *args) +{ + PyObject *value; + int state= 1; + + if(!PyArg_ParseTuple(args, "O|i:permanent_tag", &value, &state)) + return NULL; + + if (PyFunction_Check(value)) { + PyObject **dict_ptr= _PyObject_GetDictPtr(value); + if (dict_ptr == NULL) { + PyErr_SetString(PyExc_ValueError, + "bpy.app.handlers.permanent_tag wasn't able to " + "get the dictionary from the function passed"); + return NULL; + } + else { + if (state) { + /* set id */ + if (*dict_ptr == NULL) { + *dict_ptr= PyDict_New(); + } + + PyDict_SetItemString(*dict_ptr, PERMINENT_CB_ID, Py_None); + } + else { + /* clear id */ + if (*dict_ptr) { + PyDict_DelItemString(*dict_ptr, PERMINENT_CB_ID); + } + } + } + + Py_INCREF(value); + return value; + } + else { + PyErr_SetString(PyExc_ValueError, + "bpy.app.handlers.permanent_tag expected a function"); + return NULL; + } +} + +static PyMethodDef meth_bpy_app_handlers_permanent_tag= {"permanent_tag", (PyCFunction)bpy_app_handlers_permanent_tag, METH_VARARGS, bpy_app_handlers_permanent_tag_doc}; + + + + static PyObject *py_cb_array[BLI_CB_EVT_TOT]= {NULL}; static PyObject *make_app_cb_info(void) @@ -83,10 +153,13 @@ static PyObject *make_app_cb_info(void) } PyStructSequence_SET_ITEM(app_cb_info, pos, (py_cb_array[pos]= PyList_New(0))); } - if (app_cb_info_fields[pos].name != NULL) { + if (app_cb_info_fields[pos + APP_CB_OTHER_FIELDS].name != NULL) { Py_FatalError("invalid callback slots 2"); } + /* custom function */ + PyStructSequence_SET_ITEM(app_cb_info, pos++, (PyObject *)PyCFunction_New(&meth_bpy_app_handlers_permanent_tag, NULL)); + return app_cb_info; } @@ -120,12 +193,46 @@ PyObject *BPY_app_handlers_struct(void) return ret; } -void BPY_app_handlers_reset(void) +void BPY_app_handlers_reset(const short do_all) { int pos= 0; + if (do_all) { for (pos= 0; pos < BLI_CB_EVT_TOT; pos++) { - PyList_SetSlice(py_cb_array[pos], 0, PY_SSIZE_T_MAX, NULL); + /* clear list */ + PyList_SetSlice(py_cb_array[pos], 0, PY_SSIZE_T_MAX, NULL); + } + } + else { + /* save string conversion thrashing */ + PyObject *perm_id_str= PyUnicode_FromString(PERMINENT_CB_ID); + + for (pos= 0; pos < BLI_CB_EVT_TOT; pos++) { + /* clear only items without PERMINENT_CB_ID */ + PyObject *ls= py_cb_array[pos]; + Py_ssize_t i; + + PyObject *item; + PyObject **dict_ptr; + + for(i= PyList_GET_SIZE(ls) - 1; i >= 0; i--) { + + if ( (PyFunction_Check((item= PyList_GET_ITEM(ls, i)))) && + (dict_ptr= _PyObject_GetDictPtr(item)) && + (*dict_ptr) && + (PyDict_GetItem(*dict_ptr, perm_id_str) != NULL)) + { + /* keep */ + } + else { + /* remove */ + /* PySequence_DelItem(ls, i); */ /* more obvious buw slower */ + PyList_SetSlice(ls, i, i + 1, NULL); + } + } + } + + Py_DECREF(perm_id_str); } } |